//
// prep.cpp
// prep
// Prepares APPDIC data for use during run-time execution
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

/////
//
// Prep program
//
//	Takes all information contained in the APPDIC database
//	and converts it into runtime files.  Also manages any
//	database changes that need to occur.
//

// Database-Specific Weirdisms And Things That Just Generally Suck!
// TODO: for Protea, this information in comments will help to recreate the missing database drivers
//
// INTERBASE :
//		1.	Requires CREATE TABLE statements to be committed before adding 
//			data to that table.
//		2.	SET TRANSACTION ISOLATION LEVEL doesn't work the way you would
//			expect it to, because it uses proprietary locking types.
//		3.	Requires and Exclusive Lock on the database to create FOREIGN
//			KEY references.
//		4.	AUTONUMBER data type is implemented by creating a GENERATOR object,
//			then calling the GEN_ID function on all INSERT statements for
//			a field of that type.
//		5.	There is no DROP GENERATOR statement.  GENERATORS must be dropped
//			by accessing a database System table directly to delete the record.
//
// MS SQL 7:
//		1.	DATE, TIME, and DATETIME are not supported correctly, it uses 
//			proprietary date and time formats.
//		2.	ALTER TABLE DROP defaults to dropping CONSTRAINTS instead of 
//			COLUMNS as the standard defines.
//		3.	AUTONUMBER data type is implemented through the use of the
//			IDENTITY property of a column.
//		4.  Must drop DEFAULT constraint before removing a field from a table
//
// ORACLE :
//		1.	DATE, TIME, and DATETIME are not supported correctly, it uses 
//			proprietary date and time formats.  Furthermore, it has a field 
//			type DATE which is actually the TIMESTAMP type.
//		2.	They messed up initially implementing VARCHAR, so must use 
//			VARCHAR2 data type.
//		3.	AUTONUMBER data type is implemented by creating a SEQUENCE object,
//			then calling the NEXTVAL function on all INSERT statements for
//			a field of that type.
//
// DB2 :
//		1.	Does not support ALTER TABLE DROP COLUMN syntax.  In order to drop
//			a column from a table, the user must create a new table without 
//			the column to drop, and then manually copy data between them.
//		2.	AUTONUMBER data type is implemented through the use of the GENERATED
//			BY DEFAULT AS IDENTITY clause on a CREATE TABLE statement.
//
// Postgres :
//		1.	No support for primary keys until version 7.2+.
//
// Access :
//		1.  Won't rollback table creation commands
//
// MySQL :
//		1.  Won't rollback table creation commands
//		2.  No support for data binding
//
// Doesn't anybody support the ANSI/ISO SQL 92 syntax?

#define IMPORT

// TODO: Fix 	bool same_database = true;
bool same_database = true;
char lockfilename[ 2048 ];

#include <iostream>
#include <fstream>
#include <time.h>
#include "../SDK/ToolsInterface.h"
#include "../Fields/field_defns.h"
#include "../Database/prepSQL.h"
#include "../Security/hash.h"
#include "../defns.h"
using namespace ProteaApp1;

bool AnalysisOnly = false;
bool SkipDatabase = false;
bool nomove = false;
bool bootstrap = false;

appl_defn current_app;

// Forward declarations
class constraint_info;



const long CHANGE_NONE = 0;
const long CHANGE_TYPE = 1;
const long CHANGE_LENGTH = 2;
const long CHANGE_NAME = 4;
const long CHANGE_DEFAULT = 16;
const long CHANGE_FLAGS = 32;
const long CHANGE_DECPLACES = 64;
const long CHANGE_CREATED = 128;
const long CHANGE_DELETED = 256;
const long CHANGE_DS = 512;

// Constants for new system buffer numbers
const long NEW_SYSTEM_VALUES = 10000;
const long NEW_LITERAL_DEFN = 10001;

// Enum for possible database changes
enum change_type {
	delete_table = 0,
	create_table = 1,
	update_table = 2,
	update_table_name_change = 3,
	delete_field = 4,
	create_field = 5,
	delete_primary = 6,
	create_primary = 7,
	delete_constraint = 8,
	create_constraint = 9,
	delete_autonumber = 10,
	create_autonumber = 11
};



struct field_change {
	field_change( long new_type, long index ) {
		change_type = new_type;
		number = index;
	};

	long number;
	long change_type;
	field_change* next;
};

struct format_change {
	void AddFieldChange( field_change* new_record ) {
		new_record->next = NULL;
		if (last) {
			last->next = new_record;
			last = new_record;
		} else
			last = ds = new_record;

		type |= CHANGE_DS;
	};

	long type;
	buffer_format* new_buffer;
	buffer_format* old_buffer;
	field_change* ds;
	field_change* last;
};



// Node object for linked list of database changes
struct XCDBChangesList {
	XCDBChangesList() { executed = false; };

	int					format;		// Format number
	int					field;		// Field number
	change_type			type;		// Change type
	constraint_info*	cons_data;	// Constraint information
	bool				executed;	// Was this change executed?
	XCDBChangesList*	next;		// Pointer to next record
};


// For creation of the Literal tables for reporting
struct MakeLiteralsInfo {
	buffer_format* format;
	buffer_ds* ds;
	buffer_field* fld[4];
	buffer_db_tables table;
};


// Class for the Prep application
class XCPrepInterface : public XCAppInterface {
public:
	// Constructor
	XCPrepInterface() { changes = NULL; query = new XCPrepSQLGen( this ); new_data = NULL; };
	// Destructor
	~XCPrepInterface();

	void Initialize( char* process_name, int appnumber, char* FileExtension = NULL ) {
		query->SetRealApp( appnumber );
		XCAppInterface::Initialize( process_name, appnumber, FileExtension );
	};

	// Adds constraint update records to a new table
	bool AddConstraints( int format );

	// Compares constraints in the old/new database definitions and determines
	// which constraints need to be added/deleted/rebuilt
	void CheckConstraints( int format, XCPrepInterface* olddb );
	/// Checks the name/type/length of data fields to determine if the table needs to be rebuilt.
	void CheckFields( int format, XCPrepInterface* olddb );
	// Determines if tables have been added/deleted/updated.  Marks tables for
	// update that have change databases.
	void CheckTables( XCPrepInterface* olddb );

	/*	UpdateTable is called when a major change is to be made on the table,
		such as changing field types or sizes.
		It accomplishes this change through three steps:
			1)	Create a new database table 
			2)	Move the data to the new table
			3)	Mark the old table for deletion
	*/
	void UpdateTable( int format, XCPrepInterface* olddb, bool name_change = false );

	void FirstCheck();

	void MakeUEInfo();
//	void MakeXMLInterface();

	void Analysis( XCPrepInterface* olddb );
	void AnalyzeFields( XCFileTransport* transport, XCPrepInterface* olddb );
	void AnalyzeFormats( XCFileTransport* transport, XCPrepInterface* olddb );
	void AnalyzeFormatFields( format_change& change_info, XCPrepInterface* olddb );

	void OutputFormatChange( XCFileTransport* transport, format_change& change_info );

	void write_Appdic_File( XCTxn* dbtxn, long buftype, char* appl, bool nowhere );
	void WriteStructure( int format_number, char* filename, XCTxn* dbtxn, bool nowhere = false );
	void MoveOldFiles( bool remove_only );

	// Performs all database changes that have been requested.  If any of the changes fail, it will roll back all other changes
	void AffectChanges( bool commit, XCPrepInterface* olddb, XCPrepInterface* appdic );

	XCDBChangesList* AddChange( change_type new_type, XCBuffer* new_format, XCField* new_field );
	XCDBChangesList* AddChange( change_type new_type, int format, XCField* new_field, constraint_info* cons_info = NULL );

	// Drops foreign keys from a table
	void DropForeignKeys( XCPrepInterface* changes, buffer_format* desc );

	// Writes the data areas for buffer/data_structures/fields
	void LoadPrepInfo( XCTxn* dbtxn );
	void LoadAppInfo( XCTxn* dbtxn );
	void LoadBufferInfo( XCTxn* dbtxn );
	void IndexBuffer( buffer_format* format );
	void LoadDatabaseInfo( XCTxn* dbtxn );
//	void LoadScreenInfo( XCTxn* dbtxn );
	void LoadTransactionInfo( XCTxn* dbtxn );
	void LoadMiscInfo( XCTxn* dbtxn );
	void LoadSystemValueInfo( XCTxn* dbtxn, buffer_format* format );
	void LoadLiteralInfo( XCTxn* dbtxn, buffer_format* format );
	void LoadUserExitInfo( XCTxn* dbtxn );

	void MakeLiterals( XCTxn* dbtxn, XCTxn* app_txn );
	void MakeLiteralsCreateField( MakeLiteralsInfo& info, const char* name, int type, int fld_number );

	buffer_format* CreateSystemBuffers( buffer_format* buf_format );

	void WritePrepInfo( bool use_new_data );

	// Helper functions
	void FillDefaultValue( char* defaultval, buffer_ds* ds, buffer_field* field, buffer_format* format, buffer_format* buf_literal = NULL );
	void SetAllocSize( buffer_ds* buf_ds, buffer_format* buf_format );
	void ResetChangeFlags( XCTxn* dbtxn );

	// Find field record offset
	buffer_field* GetLocalFieldInfo( int field_number );
	// Find literal field record offset
	buffer_field* GetLocalLiteral( buffer_format* literal_buf, int literal );
	buffer_format* GetLocalFormatInfo( int format_number );
//	buffer_screen_defn* GetLocalScreenInfo( long format_number, long term_type );

	// Counts the number of primary key relations
	long CountPrimary( buffer_db_relate* relations, int maxval );

	// Query generator for this application
	XCPrepSQLGen* query;

	// Do this so that error messages work out the way they should
	void SetCustomizerApp() { myappnum = 1; };

	bool IsSystemBuffer( int number ) {
		return (number >= NEW_SYSTEM_VALUES && number < (NEW_LITERAL_DEFN + MAX_LANGUAGES));
	};

protected:
	// Contains a list of all changes scheduled to be made on the databases
	XCDBChangesList* changes;

	long datasize;
	char* new_data;
	buffer_header*	header;

	XCcrc32 MyCRC;
};






class constraint_info {
public:
	constraint_info( buffer_db_relate* list, int rel_count ) {
		count = rel_count;
		constraint = list;
	};

	bool NewConstraint( XCBuffer* buf, XCPrepInterface* app ) {
		if (constraint->type == dbr_default) {
			XCDBDriverDescr* desc = app->GetDBDriverDesc( buf );
			if (!desc->uses_default_constraint)
				return false;
		} else if ( constraint->type != dbr_primary_key && constraint->type != dbr_unique_key && constraint->type != dbr_foreign_key && constraint->type != dbr_index_key)
			return false;
		app->query->MakeADDCON( buf, constraint, count, app );
		return true;
	};
	
	bool DelConstraint( XCBuffer* buf, XCPrepInterface* app ) {
		if (constraint->type == dbr_default) {
			XCDBDriverDescr* desc = app->GetDBDriverDesc( buf );
			if (!desc->uses_default_constraint)
				return false;
		} else if ( constraint->type != dbr_primary_key && constraint->type != dbr_unique_key && constraint->type != dbr_foreign_key && constraint->type != dbr_index_key)
			return false;

		app->query->MakeDROPCON( buf, constraint, count, app );
		return true;
	};

	long GetType() { assert(constraint); return constraint->type; };

private:
	buffer_db_relate* constraint;
	int count;
};



struct need_to_move {
	char* filename;
	bool nowhere;
} MoveIt[ 25 ];
int MoveCount = 0;




//
// Global variables
//
XCAppOptions myopts;

XCPrepInterface	appdic;
XCPrepInterface*  olddb = NULL;
XCPrepInterface*  newdb = NULL;

int appl_number = -1;
char* argvpath = NULL;
XCBuffer* app_info = NULL;

// Signal Handler
PREP_SIG_HANDLER;

long GetCount( XCPrepSQLGen* query, XCTxn* dbtxn, XCBuffer* buf, long fld_number, char* info )
{
	long retval = 0;
	XCBuffer* bCount;
	if (fld_number)
		bCount = query->MakeCOUNT( buf, buf->GetField( fld_number ) );
	else
		bCount = query->MakeCOUNT( buf );

	XCField* list[3];
	XCCompare comp[2];
	list[0] = buf->GetField( FLD_APPL_NBR );
	list[0]->SetLong( 0 );
	list[1] = app_info->GetField( FLD_APPL_NBR );
	list[2] = NULL;
	comp[1].logical = logic_or;
	query->AddWHERE( list, comp );

	XCDBStatement* stmt = dbtxn->ExecuteSQL( query );
	if ( !stmt->Eof() )
	{
		retval = bCount->GetFieldByIndex( 0 )->GetLong();
		appdic.ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "- Processing %ld %s records...", retval, info );
	};
	stmt->Release();
	bCount->Release();

	return retval;
};



long PrepAlign( long size ) {
	// Set our packing right here!
	int packing = 2;		// 32 bit packing
	if (packing) {
		int temp = size;
		temp += (1 << packing) - 1;
		temp >>= packing;
		temp <<= packing;

		size = temp;
	};

	return size;
};



XCPrepInterface::~XCPrepInterface() {
//	cout << "Delete db changes" << endl;

	XCDBChangesList *temp = changes;
	XCDBChangesList *temp2;
	while (temp) {
		temp2 = temp;
		temp = temp->next;
		delete temp2;
	};

	delete query;

	if (new_data)
		delete [] new_data;

//	cout << "Done" << endl;
};





void XCPrepInterface::MakeUEInfo() {
//	cout << "MakeUEInfo" << endl;

	int i, on_buffer;
	XCBuffer* buf;
	fstream ueout;

	INFUNC( XCPrepInterface::MakeUEInfo, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Make UE Info" );

//	TriggerUserExit( 1, NULL, 0 );

	ueout.open( "UEDefn.h", ios::out );
	ueout << "//" << endl;
	ueout << "// User Exit Structure Definitions" << endl;
	ueout << "//" << endl;
	ueout << "#pragma pack(1)" << endl << endl;

	ueout << "struct t_user_info {" << endl;
	ueout << "\tconst char* username;" << endl;
	ueout << "};" << endl << endl;

	ueout << "struct ue_info {" << endl;
	ueout << "\t// General" << endl;
	ueout << "\tlong\t\ttransaction_number;" << endl;
	ueout << "\tt_user_info\tuser_info;" << endl;
	ueout << endl << "\t// Storage" << endl;
	ueout << "\tchar*\t\tprocess_storage;" << endl;
	ueout << endl << "\t// Return information" << endl;
	ueout << "\tchar\t\tmessage[256];" << endl;
	ueout << "\tchar\t\tfield_name[24];" << endl;
	ueout << endl << "\t// Buffer pointers" << endl;
	ueout << "\tvoid*\t\tbuffer[10];" << endl;
	ueout << "\tint\t\tbuffer_number[10];" << endl;
	ueout << "\t// Available for Protea interfacing" << endl;
	ueout << "\tvoid* buffer_ptr[10];" << endl;
	ueout << "\tvoid* vars;" << endl;
	ueout << "};" << endl << endl;

	XCBuffer* fdb = appdic.CreateBuffer( FMT_FORMAT_DESCRIPTR, false );

	buffer_format* fd = (buffer_format*)FixOffset( appdic_header->formats.offset );
	for (on_buffer=0; on_buffer < appdic_header->formats.count; on_buffer++) {
		int array_len = 0;
		int dummy = 0;
		int alloc;

		if (!IsSystemBuffer( fd->number )) {
			char tmp_field[ 22 ];
			strcpy( tmp_field, fd->name );

			if (fd->dd_offset) {
				buffer_db_tables* dd = (buffer_db_tables*)FixOffset( fd->dd_offset );
				if (dd->view_flag >= 0) {
					tmp_field[ strlen( tmp_field ) - 2 ] = 0;
				};
			};
	//		cout << tmp_field << endl;

			fdb->GetField( FLD_FORMAT_NUMBER )->SetLong( fd->number );
			fdb->GetField( FLD_FORMAT_NAME )->SetString( tmp_field );

			buf = CreateBuffer( fd->number, false );

			XCBufferList list;
			list.Add( fdb );
			list.Add( buf );
	//		TriggerUserExit( 2, &list, 0 );

			// Create the buffer header
			ueout << "struct BUFFER_" << tmp_field << " {" << endl;;
			for (i=0; i<buf->GetFieldCount(); i++) {
				XCField* field = buf->GetFieldByIndex(i);

				switch( field->GetType() ) {
					case S_INTEGER:
						ueout << "\tshort int\t";
						array_len = -field->GetLength();
						break;

					case D_INTEGER:
					case AUTONUMBER:
						ueout << "\tint\t\t";
						array_len = -field->GetLength();
						break;

					case DEC_FLOAT:
						ueout << "\tfloat\t\t";
						array_len = -field->GetLength();
						break;

					case DEC_DOUBLE:
						ueout << "\tdouble\t\t";
						array_len = -field->GetLength();
						break;

					default:
						ueout << "\tchar\t\t";
	//					array_len = field->GetLength()+1;
						array_len = field->GetAllocSize();
						break;
				};
				alloc = field->GetAllocSize();
				ueout << field->GetFieldName();
				if (array_len > 0)
					ueout << "[" << array_len << "]";
				ueout << ";" << endl;

				// Check to see if we need to create a dummy variable
				int dif = alloc - abs(array_len);
				if (dif) {
					ueout << "\tchar\t\tDummy" << dummy++;
					if (dif > 1)
						ueout << "[" << (alloc - abs(array_len)) << "]";
					ueout << ";" << endl;
				};
			};
			ueout << "};" << endl << endl;
			buf->Release();
		};

		fd++;
	};

	fdb->Release();

	ueout << "#pragma pack()" << endl;
	ueout.close();

//	TriggerUserExit( 3, NULL, 0 );

	OUTFUNC()
};



//  Modify database
//////////////////////////////////////////////////////////////////////////////




//
// CountPrimary
//
//	Counts the number of primary key relations
//
long XCPrepInterface::CountPrimary( buffer_db_relate* relations, int maxval ) {
	int primary_keys = 0;
	int count = 0;
	buffer_db_relate* temp = relations;
	while (count < maxval && temp->type == dbr_primary_key) {
		primary_keys++;
		temp++;
		count++;
	};

	return primary_keys;
};






//
// AddConstraints
//
//	Adds constraint update records to a new table
//
bool XCPrepInterface::AddConstraints( int format ) {
	INFUNC( XCPrepInterface::AddConstraints, this )

	bool retval = false;	// returns true if autonumber field exists
	int primary_keys;
	int count;
	constraint_info* data;
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "AddCons: %d", format );

	// Build a list of db_relate records for this format...
	buffer_format* bformat = GetFormatInfo( format );
	buffer_db_tables* table = (buffer_db_tables*)FixOffset( bformat->dd_offset );
	buffer_db_relate* relate = (buffer_db_relate*)FixOffset( table->relate_offset );

	primary_keys = 0;
	count = 0;
	buffer_db_relate* temp = relate;
	while (count < table->relate_count && (temp->type == dbr_primary_key || temp->type == dbr_autonumber ) ) {
		if ( temp->type == dbr_primary_key )
			primary_keys++;
		if ( temp->type == dbr_autonumber )
			retval = true;
		temp++;
		count++;
	};

	if (primary_keys) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Create primary key:" );

		temp = relate;
		for (int i=0; i<primary_keys; i++) {
			buffer_field* field = (buffer_field*)FixOffset( temp->field_offset );
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "\t%s", field->name );
			temp++;
		};

		data = new constraint_info( relate, primary_keys );
		AddChange( create_primary, format, NULL, data );
	};

	while (count < table->relate_count) {
/*
		buffer_field* field = (buffer_field*)&new_data[ temp->field_offset ];
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Add Constraint: %s", field->name );
*/
		data = new constraint_info( temp, 1 );
		AddChange( create_constraint, format, NULL, data );

		temp++;
		count++;
	};

	// Now, add the "default" constraints for SQL Server
	buffer_ds* ds = (buffer_ds*)FixOffset( bformat->ds_offset );
	for (int i=0; i<bformat->ds_count; i++) {
		// All types, other than AUTONUMBER have a default
		if (ds->data_type != AUTONUMBER) {
			buffer_db_relate* list = new buffer_db_relate;
			memset( list, 0, sizeof( buffer_db_relate ) );

			list->field_offset = ds->field_offset;
			list->type = dbr_default;
			constraint_info* cons = new constraint_info( list, 1 );
			AddChange( create_constraint, format, NULL, cons );
		};
		ds++;
	};
	OUTFUNCRET( retval )
};






//
// CheckConstraints
//
//	Compares constraints in the old/new database definitions and determines
// which constraints need to be added/deleted/rebuilt
//
void XCPrepInterface::CheckConstraints( int format, XCPrepInterface* olddb ) {
	int i;
	bool primary_changed;
	constraint_info* data;
	int new_count, old_count;
	int new_prim, old_prim;
	int temp_new=0, temp_old=0;
	buffer_field *new_field, *old_field;

	INFUNC( XCPrepInterface::CheckConstraints, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "CheckCons: %d", format );

	// Build a list of db_relate records for this format in both the old and the new...

	// Old
	buffer_format* bformat = olddb->GetFormatInfo( format );
	buffer_db_tables* table = (buffer_db_tables*)olddb->FixOffset( bformat->dd_offset );
	buffer_db_relate* old_relate = (buffer_db_relate*)olddb->FixOffset( table->relate_offset );
	old_count = table->relate_count;
	old_prim = CountPrimary( old_relate, old_count );

	// New
	bformat = GetFormatInfo( format );
	table = (buffer_db_tables*)FixOffset( bformat->dd_offset );
	buffer_db_relate* new_relate = (buffer_db_relate*)FixOffset( table->relate_offset );
	new_count = table->relate_count;
	new_prim = CountPrimary( new_relate, new_count );

	// Check to see if fields added to the primary key
//	cout << "Check primary keys" << endl;
	if (old_prim != new_prim) {
//		cout << "keys differ: " << new_prim << " vs " << old_prim << endl;
		primary_changed = true;

		temp_new += new_prim;
		temp_old += old_prim;
	} else {
		// Check to see that the fields in primary key are the same!
		primary_changed = false;
		for (i=0; i<new_prim; i++) {
			new_field = (buffer_field*)FixOffset( new_relate[temp_new].field_offset );
			old_field = (buffer_field*)olddb->FixOffset( old_relate[temp_old].field_offset );

			if (new_field->number != old_field->number)
				primary_changed = true;

			temp_new++;
			temp_old++;
		};
	};

	// If primary key changed, need to delete old primary key and add new primary key
	if (primary_changed) {
//		cout << "Primary key changed!" << endl;
		if (old_prim) {
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Delete primary key" );
			data = new constraint_info( old_relate, old_prim );
			AddChange( delete_primary, format, NULL, data );
		};
		
		// only add new primary key if there will be a primary key for the new table
		if (new_prim) {
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Create primary key" );
			data = new constraint_info( new_relate, new_prim );
			AddChange( create_primary, format, NULL, data );\
		};
	};

	// Walk down the remaining values
//	cout << "Check remaining values: old = " << old_count - temp_old << " new = " << new_count - temp_new << endl;
	int todo = 0;
	while (temp_new < new_count || temp_old < old_count) {
		if (temp_new < new_count)
			new_field = (buffer_field*)FixOffset( new_relate[temp_new].field_offset );
		else
			new_field = NULL;

		if (temp_old < old_count)
			old_field = (buffer_field*)olddb->FixOffset( old_relate[temp_old].field_offset );
		else
			old_field = NULL;

		// Basically, check to see if the relations match
		if (old_field && !new_field)
			todo = -1;
		else if (new_field && !old_field)
			todo = 1;
		else if (new_field->number > old_field->number && old_relate[temp_old].type <= new_relate[temp_new].type)
			todo = -1;
		else if (new_field->number < old_field->number && new_relate[temp_new].type <= old_relate[temp_old].type)
			todo = 1;
		else
			todo = 0;

		switch( todo ) {
			case -1:
				// Delete a constraint
//				cout << "Delete constraint" << endl;
				data = new constraint_info( &old_relate[temp_old], 1 );
				if ( old_relate[temp_old].type != dbr_autonumber )
					AddChange( delete_constraint, format, NULL, data );
				else
					AddChange( delete_autonumber, format, NULL, data );
				temp_old++;
				break;

			case 0:
				// Constraints are the same
				temp_new++;
				temp_old++;
				break;

			case 1:
				// Add a new constraint
//				cout << "Add constraint" << endl;
				data = new constraint_info( &new_relate[temp_new], 1 );
				if ( new_relate[temp_new].type != dbr_autonumber )
					AddChange( create_constraint, format, NULL, data );
				else
					AddChange( create_autonumber, format, NULL, data );
				temp_new++;
				break;
		};
	};

	OUTFUNC()
};




//
// CheckFields
//
//	Checks the name/type/length of data fields to determine if the table
// needs to be rebuilt.
//
// Assumptions:
//
// Table will remain in the same database.  We can use database
// driver descriptor to determine the level of update required
//
void XCPrepInterface::CheckFields( int format, XCPrepInterface* olddb ) {
	XCBuffer *newbuf = NULL, *oldbuf = NULL;
	XCField *new_field = NULL, *old_field = NULL;
	int i;
	int changed = CHANGE_NONE;

	INFUNC( XCPrepInterface::CheckFields, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "CheckFields: %d", format );

	try {
		newbuf = CreateBuffer( format, true );
		oldbuf = olddb->CreateBuffer( format, true );
//		printf( "Check fields: %s\n", newbuf->GetFormatName() );

		//
		// Check for change in field types/sizes
		//	If we are simply adding/dropping fields then we can do those changes on the
		//	fly (usually).  If we are changing field types or sizes, then we have to go through a
		//	big ordeal and should just skip adding/dropping fields as it will be done in
		//	the process of altering the field types
		//
		for (i=0; i<newbuf->GetFieldCount() && !changed; i++) {
			new_field = newbuf->GetFieldByIndex( i );
			old_field = oldbuf->GetField( new_field->GetFieldNumber() );

			// We are only concerned about fields that are changed
			if (new_field && old_field) {
				if (new_field->GetType() != old_field->GetType())
				{
					// Changing an existing field to AUTONUMBER type is not permitted.  
					// AUTONUMBER fields can only be added or deleted.
					if (new_field->GetType() == AUTONUMBER )
						RAISETOOLSERROR( ERR_AUTONUMBER_CHANGE );
					changed |= CHANGE_TYPE;
				};
				if (new_field->GetLength() != old_field->GetLength())
					changed |= CHANGE_LENGTH;
				if (strcmp( new_field->GetFieldName(), old_field->GetFieldName()) != 0 )
					changed |= CHANGE_NAME;
				if ( (new_field->GetFlags() & FLAG_ALLOW_NULL) != (old_field->GetFlags() & FLAG_ALLOW_NULL) )
					changed |= CHANGE_FLAGS;
			};
		};

		// Check for dropped fields...
		for (i=0; i<oldbuf->GetFieldCount() && !changed; i++) {
			old_field = oldbuf->GetFieldByIndex( i );
			new_field = newbuf->GetField( old_field->GetFieldNumber() );

			// Was a field dropped, and can we handle it?
			if (old_field && !new_field) {
				XCDBDriverDescr* desc = GetDBDriverDesc( newbuf );
				if (!desc->can_drop_fields)
					changed |= CHANGE_DELETED;
			};
		};

		if (changed) {
			if (changed != CHANGE_DELETED) {
				ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "  Field changed: %s (%s)  size: %d->%d  type: %d->%d  flags: %d->%d",
					new_field->GetFieldName(), 
					old_field->GetFieldName(),
					old_field->GetLength(), new_field->GetLength(),
					old_field->GetType(), new_field->GetType(),
					old_field->GetFlags(), new_field->GetFlags() );
			};

			// We know that we have to make some major changes...
			UpdateTable( newbuf->GetFormatNumber(), olddb );
		} else {
			for (i=0; i<newbuf->GetFieldCount(); i++) {
				new_field = newbuf->GetFieldByIndex( i );
				old_field = oldbuf->GetField( new_field->GetFieldNumber() );
//ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Check Fields NEW : %ld, OLD : %ld", new_field, old_field );
				if (new_field && !old_field) {
					if ( new_field->GetType() != AUTONUMBER )
					{
						ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Create field: %s", new_field->GetFieldName() );
						AddChange( create_field, newbuf, new_field);
					}
					else
					{
						// Rebuild the table for a new Autonumber field
						ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Create autonumber field: %s", new_field->GetFieldName() );
						UpdateTable( newbuf->GetFormatNumber(), olddb );
					};
				};
			};

			for (i=0; i<oldbuf->GetFieldCount(); i++) {
				old_field = oldbuf->GetFieldByIndex( i );
				new_field = newbuf->GetField( old_field->GetFieldNumber() );
				if (!new_field) {
					ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Delete field: %s", old_field->GetFieldName() );
					AddChange( delete_field, oldbuf, old_field );

					// SQL Server requires dropping the default constraint...
					// All fields, other than AUTONUMBER have defaults
					if (old_field->GetType() != AUTONUMBER) {
						buffer_db_relate* list = new buffer_db_relate;
						memset( list, 0, sizeof( buffer_db_relate ) );
						list->field_offset = old_field->GetDSInformation()->field_offset +
							olddb->APPDIC_DATA - APPDIC_DATA;		// Hack to get pointer to old instead of new
						list->type = dbr_default;
						constraint_info* cons = new constraint_info( list, 1 );
						AddChange( delete_constraint, oldbuf->GetFormatNumber(), old_field, cons );
					};
				};
			};

			CheckConstraints( format, olddb );

		};
	} catch( XCToolsError& ) {
		CleanupObjects( 2, &newbuf, &oldbuf );
		throw;
	};

	CleanupObjects( 2, &newbuf, &oldbuf );

	OUTFUNC()
};





void XCPrepInterface::FirstCheck()
{
	INFUNC( XCPrepInterface::FirstCheck, this )

	int i;

	// Check all format descriptors
	buffer_format* desc = (buffer_format*)FixOffset( appdic_header->formats.offset );

	for (i=0; i<appdic_header->formats.count; i++) {
		buffer_db_tables* table = (buffer_db_tables*)FixOffset( desc->dd_offset );

		if (desc->type < 3) {
			int myformat = desc->number;

			// Check to see if we have dataset descriptor records
			if (!table) {
				ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d missing dataset descriptor record!", myformat );
				RAISETOOLSERROR( ERR_NO_DD_RECORD );
			};

			// Append view flag on format_name
			if (table->view_flag >= 0) {
//				cout << "New format name: " << desc->format_name << endl;
//				cout << "View flag: " << dd->view_flag << endl;
				sprintf( &desc->name[strlen(desc->name) ], "%2.2ld", table->view_flag );
//				cout << "Concated name: " << desc->format_name << endl;
//				desc->view_flag = true;
			};
		};
		desc++;
	};

//	CleanupObjects( 1, &desc );

	OUTFUNC()
};




//
// CheckTables
//
//	Determines if tables have been added/deleted/updated.  Marks tables for
// update that have change databases.
//
void XCPrepInterface::CheckTables( XCPrepInterface* olddb )
{
	int i;
	//XCTxn* txn = NULL;

	buffer_format *old_desc, *new_desc;
	buffer_db_tables *new_dd, *old_dd;

	INFUNC( XCPrepInterface::CheckTables, this )

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Check database structures..." );

	// Check all format descriptors

	new_desc = (buffer_format*)FixOffset( appdic_header->formats.offset );
	for (i=0; i<appdic_header->formats.count; i++) {
		int myformat = new_desc->number;

		// Is this a database table
		if (new_desc->type < 3) {
			bool names_match;

			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Format: %s,%d,%d", new_desc->name, new_desc->number, new_desc->dd_offset);

			// Check for format descriptor record in old definition
			old_desc = olddb->GetFormatInfo( myformat );

			// Check to see if we have a matching format number of database type
			if (old_desc && old_desc->type < 3) {
				// Check to see if we have dataset descriptor records
				if (!new_desc->dd_offset || !old_desc->dd_offset) {
					ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d missing dataset descriptor record!", myformat );
					RAISETOOLSERROR_1PARAM( ERR_NO_DD_RECORD, myformat );
				};

				// Check for matching database numbers
				new_dd = (buffer_db_tables*)FixOffset( new_desc->dd_offset );
				old_dd = (buffer_db_tables*)olddb->FixOffset( old_desc->dd_offset );

				assert( new_dd );
				assert( old_dd );
				
				assert( new_dd->db_offset );
				assert( old_dd->db_offset );

				// If database numbers match, then go ahead and do stuff

				// Append view flag on format_name
				if (new_dd->view_flag >= 0) {
//						cout << "New format name: " << new_desc->name << endl;
//						cout << "View flag: " << new_dd->view_flag << endl;
					sprintf( &new_desc->name[strlen(new_desc->name) ], "%2.2ld", new_dd->view_flag );
//						cout << "Concated name: " << new_desc->name << endl;
//						new_desc->view_flag = true;
				};

				int newtable = ((buffer_db_defn*)FixOffset( new_dd->db_offset ))->number;
				int oldtable = ((buffer_db_defn*)olddb->FixOffset( old_dd->db_offset ))->number;

				// Determine if this is a name change
				names_match = strcmp( new_desc->name, old_desc->name ) == 0;
/*
				if (!names_match) {
					printf( "NAME CHANGE DETECTED:\n" );
					printf( "Orig name: %s\n", old_desc->name );
					printf( "New name : %s\n", new_desc->name );
				};
*/
				if ((newtable == oldtable) && names_match)
					CheckFields( myformat, olddb );
				else {
					if (!names_match) {
						ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d is changing names: '%s' to '%s'", 
							myformat, old_desc->name, new_desc->name );

						// Chop off format name
						if (new_dd->view_flag >= 0)
							new_desc->name[strlen(new_desc->name)-2] = 0;
	
						// Force table name to be base format name
						new_dd->view_flag = -2;

						UpdateTable( myformat, olddb, true );
					} else {
						ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d is moving databases", myformat );
						UpdateTable( myformat, olddb );
					};
				};
			} else {
				if (!new_desc->dd_offset) {
					ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d missing dataset-descr", myformat );
					RAISETOOLSERROR_1PARAM( ERR_NO_DD_RECORD, new_desc->name );
				};

				new_dd = (buffer_db_tables*)FixOffset( new_desc->dd_offset );

				ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d needs to be created", myformat );
				AddChange( create_table, myformat, NULL );
				AddConstraints( myformat );
			};
		};

		new_desc++;
	};


	// Check all format descriptors in old (look for deletions)
	old_desc = (buffer_format*)olddb->FixOffset( olddb->appdic_header->formats.offset );
	for (i=0; i<olddb->appdic_header->formats.count; i++) {
		int myformat = old_desc->number;

		if (old_desc->type < 3 ) {
			// Check to see if we dont have matching format, or if that matching format
			// has changed to a non-database table format
			if (!(new_desc = GetFormatInfo( myformat )) || new_desc->type >= 3) {
				ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d needs to be deleted", myformat );
				AddChange( delete_table, myformat, NULL );
				olddb->DropForeignKeys( newdb, old_desc );
			};
		};

		old_desc++;
	};

	OUTFUNC()
};



//
// UpdateTable
//
//	UpdateTable is called when a major change is to be made on the table,
// such as changing field types or sizes.
//
//	It accomplishes this change through three steps:
//
//		1)	Create a new database table 
//		2)	Move the data to the new table
//		3)	Mark the old table for deletion
//
void XCPrepInterface::UpdateTable( int format_number, XCPrepInterface* olddb, bool name_change ) {
	XCBuffer *new_buf, *old_buf;
	XCTxn *new_txn = NULL, *old_txn = NULL;
	XCDBStatement *new_dbstmt, *old_dbstmt;
	buffer_format *new_desc;
	buffer_db_tables *new_dd;

	INFUNC( XCPrepInterface::UpdateTable, this )

	// 
	// We do big nasty stuff here.  We read everything from one database table and move
	// records over to another
	//
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "UpdateTable: %d", format_number );

	// Step 0.	If this table didn't previously have fields, then create the table
	old_buf = olddb->CreateBuffer( format_number, false );
	if (old_buf->GetFieldCount() == 0) {
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Buffer %d needs to be created", format_number );
		AddChange( create_table, format_number, NULL );
		AddConstraints( format_number );
		return;
	};

	// Step 1.  Create our working buffers
	new_desc = GetFormatInfo( format_number );
	new_dd = (buffer_db_tables*)FixOffset( new_desc->dd_offset );
	new_buf = CreateBuffer( format_number, true );

	// Step 2.  Update DATASET_DESCR.View_flag
	if (new_dd->view_flag == -2)
		new_dd->view_flag = -1;
	else if (new_dd->view_flag == -1) 
		new_dd->view_flag = 0;
	else {
		new_dd->view_flag++;
		if (new_dd->view_flag > 99)
			new_dd->view_flag = 0;

		new_desc->name[ strlen( new_desc->name ) - 2 ] = 0;
	};

//	cout << "New view = " << new_dd->view_flag << endl;

	if (new_dd->view_flag != -1) {
		sprintf( &new_desc->name[ strlen( new_desc->name ) ], "%2.2ld", new_dd->view_flag );
//		new_desc->view_flag = true;
	};

	if (!AnalysisOnly) {
		// Step 3.  Create New table
		new_txn = GetDatabase()->CreateTxn();
		try {
			query->MakeCREATE( new_buf );
			new_dbstmt = new_txn->ExecuteSQL( query );
			if (new_dbstmt) new_dbstmt->Release();

			// Notify that we have made changes
			AddChange( name_change ? update_table_name_change : update_table, new_buf, NULL );

			// Drop constraints on the old table
			// Add keys to the new table (primary, foreign, etc)
			if (AddConstraints( new_buf->GetFormatNumber() ) )
			{
				// AUTONUMBER field exists, create it
				query->CreateAUTONUMBER( new_buf );
				new_dbstmt = new_txn->ExecuteSQL( query );  // Check to see if this will work if the driver doesn't do anything!
				CleanupObjects( 1, &new_dbstmt );
			};

			new_txn->EndTxn( true );
		} catch( ... ) {
			new_txn->EndTxn( false );
			new_buf->Release();

			throw;
		};

		// Step 4.  Select all records and move data
		new_txn = GetDatabase()->CreateTxn();
		if (same_database)
			old_txn = new_txn;
		else
			old_txn = olddb->GetDatabase()->CreateTxn();

		// Show number of records to loop through...
//		cout << "- Processing " << GetCount( olddb->query, old_txn, old_buf, 0, old_buf->GetFormatName() ) << old_buf->GetFormatName() << " records..." << endl;
		try {
			olddb->query->MakeSELECT( old_buf );
			old_dbstmt = old_txn->ExecuteSQL( olddb->query );

			while (!old_dbstmt->Eof()) {
				new_buf->MoveCorresponding( old_buf, NULL, true );
				query->MakeINSERT( new_buf );
				new_dbstmt = new_txn->ExecuteSQL( query );
				if (new_dbstmt) new_dbstmt->Release();
				old_dbstmt->MoveNext();
			};

			old_dbstmt->Release();
			if (!same_database)
				old_txn->EndTxn( true );
			new_txn->EndTxn( true );
		} catch( ... ) {
			if (!same_database)
				old_txn->EndTxn( false );
			new_txn->EndTxn( false );
			new_buf->Release();
			old_buf->Release();
			throw;
		};

		// Now drop foreign keys on the old table
		olddb->DropForeignKeys( this, olddb->GetFormatInfo( format_number ) );
	} else	// analysis only
		AddChange( name_change ? update_table_name_change : update_table, format_number, NULL );

	new_buf->Release();
	old_buf->Release();

	OUTFUNC()
};





//
// DropForeignKeys
//
//	Drops foreign keys from a table
//
void XCPrepInterface::DropForeignKeys( XCPrepInterface* changes, buffer_format* desc ) {
	INFUNC( XCPrepInterface::DropForeignKeys, this );

	// Mark all foreign keys pointing to this buffer for deletion
	buffer_db_relate* relate = (buffer_db_relate*)FixOffset( appdic_header->db_relate.offset );
	long deadone = (char*)desc - APPDIC_DATA;
	int count = appdic_header->db_relate.count;
//	cout << "Deadone = " << deadone << "  " << desc->name << endl;
	while (count) {
		if (relate->type == dbr_foreign_key && relate->format_offset == deadone) {
//			cout << "Kill foreign constraint!" << endl;

			// Find source table
			int tcount = appdic_header->db_tables.count;
			buffer_db_tables* source = (buffer_db_tables*)
				FixOffset( appdic_header->db_tables.offset ) + (tcount - 1);

			long relate_ofs = (char*)relate - APPDIC_DATA;
//			cout << "Target ofs = " << relate_ofs << endl;

			while (tcount && (source->relate_offset > relate_ofs || source->relate_offset == 0)) {
				source--;
				tcount--;
			};

			if (!tcount) {
				cout << "Can't find source table!" << endl;
				RAISETOOLSERROR( 1 );
			};

			buffer_format* fmt = (buffer_format*)FixOffset( source->format );
			constraint_info* data = new constraint_info( relate, 1 );
			changes->AddChange( delete_constraint, fmt->number, NULL, data );
		};

		relate++;
		count--;
	};

	OUTFUNC();
};




XCDBChangesList* XCPrepInterface::AddChange( change_type new_type, XCBuffer* new_format, XCField* field ) {
	return AddChange( new_type, new_format->GetFormatNumber(), field );
};



XCDBChangesList* XCPrepInterface::AddChange( change_type new_type, int format, XCField* new_field, constraint_info* cons_info ) {
	XCDBChangesList* temp = changes;

	INFUNC( XCPrepInterface::AddChange, this )

	if (!temp) {
		temp = new XCDBChangesList;
		changes = temp;
	} else {
		while (temp->next)
			temp = temp->next;
		temp->next = new XCDBChangesList;
		temp = temp->next;
	};

	temp->format = format;
	temp->cons_data = cons_info;
	if (new_field)
		temp->field = new_field->GetFieldNumber();
	else
		temp->field = 0;
	temp->type = new_type;
	temp->next = NULL;
/*
	if (GetFormatInfo( temp->format )) {
		cout << "Table: " << GetFormatInfo( temp->format )->name << " - ";
		switch( temp->type ) {
			case delete_table:
				cout << "Drop" << endl;
				break;

			case create_table:
				cout << "Create" << endl;
				break;

			case update_table:
			case update_table_name_change:
				cout << "Update to " << GetFormatInfo( temp->format )->name << endl;
				break;

			case delete_field:
				cout << "Delete field " << temp->field << endl;
				break;

			case create_field:
				cout << "Create field " << temp->field << endl;
				break;

			case create_primary:
				cout << "Create primary key" << endl;
				break;

			case delete_primary:
				cout << "Delete primary key" << endl;
				break;

			case delete_constraint:
				cout << "Delete constraint" << endl;
				break;

			case create_constraint:
				cout << "Create constraint" << endl;
				break;

			case create_autonumber:
				cout << "Create Autonumber field" << endl;
				break;

			case delete_autonumber:
				cout << "Delete Autonumber field" << endl;
				break;

			default:
				cout << "???" << endl;
				break;
		};
	};
*/
	OUTFUNCRET( temp )
};





//
// AffectChanges
//
//	Performs all database changes that have been requested.  If any of the changes
// fail, it will roll back all other changes
//
void XCPrepInterface::AffectChanges( bool commit, XCPrepInterface* olddb, XCPrepInterface* appdic ) {
	
	INFUNC( XCPrepInterface::AffectChanges, this )

	XCDBChangesList* temp = changes;
	XCTxn *old_txn, *new_txn, *app_txn;
	XCDBStatement* stmt;
	XCBuffer* buf , *ds;
	XCField* fld;
	buffer_format* desc;
	buffer_db_tables* dd;
	bool complete;

	if (commit) {
		cout << "+-------------------------------+" << endl;
		cout << "| Implementing database changes |" << endl;
		cout << "+-------------------------------+" << endl;
	} else {
		cout << "+-------------------------------+" << endl;
		cout << "| Rolling back database changes |" << endl;
		cout << "+-------------------------------+" << endl;
	};

	ds = appdic->CreateBuffer( FMT_DATASET_DESCR, false );

	new_txn = GetDatabase()->CreateTxn();

	if (same_database)
		old_txn = new_txn;
	else
		old_txn = olddb->GetDatabase()->CreateTxn();

	// If we are running against APPDIC, just use the same transaction, or
	// we run the possibility of creating a deadlock condition
	if (appl_number == appdic->myappnum) {
		app_txn = new_txn;
	} else {
		app_txn = appdic->GetDatabase()->CreateTxn();
	};
	
	try {
		if (commit) {
			cout << "Phase 0: Drop All Constraints" << endl;
			temp = changes;
			while (temp) {
				stmt = NULL;
				buf = NULL;
				switch( temp->type ) {
					case delete_primary:
					case delete_constraint:
						buf = same_database ? newdb->CreateBuffer( temp->format, false ) : olddb->CreateBuffer( temp->format, false );
						complete = temp->cons_data->DelConstraint( buf, same_database ? newdb : olddb );
						delete temp->cons_data;
						temp->cons_data = NULL;
						if ( complete )
							stmt = old_txn->ExecuteSQL( same_database ? newdb->query : olddb->query );
						break;			

					case delete_autonumber:
						buf = olddb->CreateBuffer( temp->format, false );
						olddb->query->RemoveAUTONUMBER( buf );
						stmt = old_txn->ExecuteSQL( olddb->query );
						break;

					default:
						// Default is to do nothing at this phase
						// Added to remove gcc warnings
						break;
				};	// switch, phase 3

				if (stmt) stmt->Release();
				if (buf) buf->Release();

				// Move on to next record
				temp = temp->next;
			}; // while, phase 0

			
			
			cout << "Phase 1: Table/Field updates" << endl;
			temp = changes;
			while (temp) {
				stmt = NULL;
				buf = NULL;

				switch( temp->type ) {
					case create_table:
						buf = CreateBuffer( temp->format, true );
						cout << "\tCreating table '" << buf->GetFormatName() << "'" << endl;
						if (buf->GetFieldCount() > 0)
						{
							query->MakeCREATE( buf );
							//cout << "SQL: " << query->ViewData() << endl;
							stmt = new_txn->ExecuteSQL( query );
							temp->executed = true;
						}
						else
							cout << " *** WARNING *** Empty Table : " << buf->GetFormatName() << endl;
						break;

					case update_table:
						// Update the table.  Either rename or update view flag
						desc = GetFormatInfo( temp->format );
						dd = (buffer_db_tables*)FixOffset( desc->dd_offset );

						// Create the old table buffer
						desc = olddb->GetFormatInfo( temp->format );
						buf = olddb->CreateBuffer( temp->format, false );
						cout << "\tUpdating table '" << buf->mydesc->real_name << "'" << endl;

						// Attempt to rename the old table
						char newname[ 80 ];
						sprintf( newname, "%s_OLD", buf->mydesc->real_name );
						if (olddb->query->MakeRENAME( buf, buf->GetFormatName(), newname, same_database ? newdb : olddb )) {
							// Execute the change
							cout << "SQL: " << olddb->query->ViewData() << endl;
							old_txn->ExecuteSQL( olddb->query, SQL_NO_RESULT );

							// Alter the old buffer definition record
							strcpy( buf->mydesc->name, newname );
							buf->Release();

							// Rename the 'new' buffer
							buf = newdb->CreateBuffer( temp->format, false );
							newdb->query->MakeRENAME( buf, buf->GetFormatName(), buf->mydesc->real_name, newdb );
							cout << "SQL: " << newdb->query->ViewData() << endl;
							new_txn->ExecuteSQL( newdb->query, SQL_NO_RESULT );

							// Alter the new buffer definition record
							strcpy( buf->mydesc->name, buf->mydesc->real_name );
						} else {
							// We don't support renaming, so just bump the view flag

							// Query Dataset-descr record
							XCBuffer* buf = appdic->CreateBuffer( FMT_DATASET_DESCR, false );
							buf->GetField( FLD_FORMAT_NUMBER )->SetLong( temp->format );
							buf->GetField( FLD_APPL_NBR )->SetShort( appl_number );
							appdic->query->MakeFull( SQL_STMT_SELECT, buf );
							stmt = app_txn->ExecuteSQL( appdic->query, SQL_NO_RESULT );

							// Update view_flag
							buf->GetField( FLD_VIEW_FLAG )->SetShort( dd->view_flag );
							appdic->query->MakeFull( SQL_STMT_UPDATE, buf );
							stmt = app_txn->ExecuteSQL( appdic->query, SQL_NO_RESULT );
							buf->Release();
						};
						buf->Release();

						// FALL THROUGH

					case update_table_name_change:
						// Drop the old table
						buf = olddb->CreateBuffer( temp->format, false );
						olddb->query->MakeDROP( buf );
						cout << "SQL: " << olddb->query->ViewData() << endl;
						stmt = old_txn->ExecuteSQL( olddb->query );
						break;

					case create_field:
						buf = CreateBuffer( temp->format, true );
						fld = buf->GetField( temp->field );
						cout << "\tCreate field '" << fld->GetFieldName() << "' in table '" << buf->GetFormatName() << "'" << endl;
						query->MakeADDFIELD( buf, fld );
						//cout << "SQL: " << query->ViewData() << endl;
						stmt = new_txn->ExecuteSQL( query );
						break;

					case delete_field:
						buf = olddb->CreateBuffer( temp->format, false );
						fld = buf->GetField( temp->field );
						cout << "\tDelete field '" << fld->GetFieldName() << "' from table '" << buf->GetFormatName() << "'" << endl;
						olddb->query->MakeDROPFIELD( buf, fld );
						//cout << "SQL:" << olddb->query->ViewData() << endl;
						stmt = old_txn->ExecuteSQL( olddb->query );
						break;

					default:
						// Default is to do nothing at this phase
						// Added to remove gcc warnings
						break;
				}; // switch, phase 1

				if (stmt) stmt->Release();
				if (buf) buf->Release();

				// Move on to next record
				temp = temp->next;
			}; // while, phase 1


			
			
			cout << "Phase 2: Add Constraints, other than foreign keys" << endl;
			temp = changes;

			int last_buffer = 0;
			while (temp) {
				stmt = NULL;
				buf = NULL;
				switch( temp->type ) {
					case create_primary:
					case create_constraint:
						if (temp->cons_data->GetType() != dbr_foreign_key) {
							buf = CreateBuffer( temp->format, true );

							if (temp->cons_data->GetType() == dbr_primary_key)
								cout << "Create primary key for table '" << buf->GetFormatName() << "'" << endl;
							else if (last_buffer != buf->GetFormatNumber()) {
								cout << "Create constraints for table '" << buf->GetFormatName() << "'" << endl;
								last_buffer = buf->GetFormatNumber();
							};
							complete = temp->cons_data->NewConstraint( buf, this );
							delete temp->cons_data;
							temp->cons_data = NULL;
							//cout << "SQL: " << query->ViewData() << endl;
							if ( complete )
								stmt = new_txn->ExecuteSQL( query );
						};
						break;
					case create_autonumber:
						// Creating the AutoNumber Generator or Sequence cannot happen here.
						// It is accomplished at the time of the creation of the table.
						break;

					default:
						// Default is to do nothing at this phase
						// Added to remove gcc warnings
						break;
				}; // switch, phase 2

				if (stmt) stmt->Release();
				if (buf) buf->Release();

				// Move on to next record
				temp = temp->next;
			}; // while, phase 2




			cout << "Phase 3: Add Foreign Keys" << endl;
			temp = changes;
			while (temp) {
				stmt = NULL;
				buf = NULL;
				switch( temp->type ) {
					case create_constraint:
						if (temp->cons_data && temp->cons_data->GetType() == dbr_foreign_key) {
//							cout << "Create foreign" << endl;
							buf = CreateBuffer( temp->format, false );
							complete = temp->cons_data->NewConstraint( buf, this );
							delete temp->cons_data;

							//cout << "SQL: " << query->ViewData() << endl;
							if ( complete )
								stmt = new_txn->ExecuteSQL( query );
						};
						break;

					default:
						// Default is to do nothing at this phase
						// Added to remove gcc warnings
						break;
				};	// switch, phase 3

				if (stmt) stmt->Release();
				if (buf) buf->Release();

				// Move on to next record
				temp = temp->next;
			}; // while, phase 3



			cout << "Phase 4: Finalize table changes, drop tables" << endl;
			temp = changes;
			while (temp) {
				stmt = NULL;
				buf = NULL;
				switch( temp->type ) {
					case create_table:
						buf = CreateBuffer( temp->format, true );
						if (buf->GetFieldCount() > 0)
						{
							// Determine if this table needs finalization
							XCDBDriverDescr* desc = GetDBDriverDesc( buf );
							if (desc->requires_finalization) {
								// Call the finalization stuff
								query->MakeFINALIZE( buf, NULL, 1 );
								new_txn->ExecuteSQL( query, SQL_NO_RESULT );
							};
						}
						break;

					case delete_table:
						buf = olddb->CreateBuffer( temp->format, false );
						cout << "\tDropping table '" << buf->GetFormatName() << "'" << endl;
						if (buf->GetFieldCount() > 0 )
						{
							olddb->query->MakeDROP( buf );
							//cout << "SQL: " << olddb->query->ViewData() << endl;
							stmt = old_txn->ExecuteSQL( olddb->query );
						}
						else
							cout << " *** WARNING *** Empty Table : " << buf->GetFormatName() << endl;
						break;

					default:
						// Default is to do nothing at this phase
						// Added to remove gcc warnings
						break;
				};	// switch, phase 4

				if (stmt) stmt->Release();
				if (buf) buf->Release();

				// Move on to next record
				temp = temp->next;
			}; // while, phase 4

		} else {
			/////
			//  ROLLBACK
			/////

			// Undo changes
			temp = changes;
			while (temp) {
				if (temp->type == create_table && temp->executed) {
					buf = CreateBuffer( temp->format, false );

					XCDBDriverDescr* desc = GetDBDriverDesc( buf );
					if (desc->requires_manual_table_drop) {
						query->MakeDROP( buf );
						new_txn->ExecuteSQL( query, SQL_NO_RESULT );
					};
				};

				if (temp->type == update_table || temp->type == update_table_name_change) {
					desc = GetFormatInfo( temp->format );
					buf = CreateBuffer( temp->format, false );

					query->MakeDROP( buf );
					stmt = new_txn->ExecuteSQL( query );
					if (stmt) stmt->Release();
					buf->Release();
				}; // if

				temp = temp->next;
			}; // while
		};

		// Make the literal tables
		if (commit && appl_number != appdic->myappnum) {
			cout << "Phase 5: Create reporting literals" << endl;
			MakeLiterals( new_txn, app_txn );
		};

		// Reset changes
		if (commit)
			appdic->ResetChangeFlags( app_txn );

		// Commit our database changes
		cout << "Committing database changes..." << endl;
		if (!same_database)
			old_txn->EndTxn( true );
		new_txn->EndTxn( true );

		if (appl_number != appdic->myappnum)
			app_txn->EndTxn( true );
		ds->Release();
	} catch( ... ) {
		if (!same_database)
			old_txn->EndTxn( false );
		new_txn->EndTxn( false );
		if (appl_number != appdic->myappnum)
			app_txn->EndTxn( false );
		ds->Release();
		throw;
	};

	OUTFUNC()
};




void t_strcpy( char* dest, const char* source ) {
//	strcpy( dest, source );

	int size = strlen( source );

    while (source[ size-1 ] == ' ')
		size--;
    memcpy( dest, source, size );
    dest[ size ] = 0;

	cout << "x" << dest << "x" << endl;
};







//
// Perform analysis and output changes that occurred
//
void XCPrepInterface::Analysis( XCPrepInterface* olddb ) {
	printf( "Start Analysis...\n" );

	XCFileTransport output( this );
	char temp[ 1024 ];
	sprintf( temp, "analysis.%ld.xml", current_app.appl_nbr );
	output.Open( temp, write_only, create_always );
	output.Lock();
	
	// Write timestamp
	time_t ltime;
	tm *newtime;

	time( &ltime );
	newtime = gmtime( &ltime );

	sprintf( temp, "<Analysis Appl='%ld' Date='%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d UTC' Version='%ld'>\n",
		current_app.appl_nbr,
		newtime->tm_mon+1, newtime->tm_mday, newtime->tm_year+1900,
		newtime->tm_hour, newtime->tm_min, newtime->tm_sec, current_app.appdic_version );
	output.Send( temp );

	// First analyze the field descriptor records
	AnalyzeFields( &output, olddb );

	//
	// Next analyze the format descriptor records
	//
	AnalyzeFormats( &output, olddb );

	output.Send( "</Analysis>\n" );
	output.Unlock();
	output.Close();

	printf( "Analysis Complete!\n" );
};



//
// Analyze changes in field descriptor records
//
void XCPrepInterface::AnalyzeFields( XCFileTransport* transport, XCPrepInterface* olddb ) {
	// Start with finding new/changed fields
	buffer_field* new_field = (buffer_field*)FixOffset( appdic_header->fields.offset );
	buffer_field* old_field;
	for (int i=0; i<appdic_header->fields.count; i++) {
		// Check for format descriptor record in old definition
		old_field = olddb->GetFieldInfo( new_field->number );
		if (old_field) {
			// Next analyze the properties for this field
		} else {
//			printf( "Create field: %s\n", new_field->name );
		};
		new_field++;
	};

	// Next check for deleted fields
	old_field = (buffer_field*)olddb->FixOffset( olddb->appdic_header->fields.offset );
	for (int i=0; i<olddb->appdic_header->fields.count; i++) {
		if (!GetFieldInfo( old_field->number ))
			printf( "Delete field: %s\n", old_field->name );
		old_field++;
	};
};





void XCPrepInterface::OutputFormatChange( XCFileTransport* transport, format_change& change_info ) {
	char temp[ 1024 ];

	// Output change information
	buffer_ds* ds = NULL;
	buffer_ds* old_ds = NULL;

	if (change_info.type != 0) {
		buffer_format* format;

		transport->Send( "\t<Format Mode='" );
		if (change_info.type & CHANGE_DELETED) {
			transport->Send( "Delete" );
			format = change_info.old_buffer;
			ds = (buffer_ds*)olddb->FixOffset( format->ds_offset );
//			printf( "DELETE FORMAT: %s\n", format->real_name );
		} else if (change_info.type & CHANGE_CREATED) {
			transport->Send( "Create" );
			format = change_info.new_buffer;
			ds = (buffer_ds*)FixOffset( format->ds_offset );
//			printf( "CREATE FORMAT: %s\n", format->real_name );
		} else {
			transport->Send( "Change" );
			format = change_info.new_buffer;
			ds = (buffer_ds*)FixOffset( format->ds_offset );
			old_ds = (buffer_ds*)olddb->FixOffset( change_info.old_buffer->ds_offset );

//			printf( "CHANGE FORMAT: %s\n", format->real_name );
		};

		sprintf( temp, "' Name='%s' Number='%ld' Type='", format->name, format->number );
		transport->Send( temp );

		switch( format->type ) {
			case 0:
			case 1:
				transport->Send( "Database Table" );
				break;

			case 3:
				transport->Send( "Screen" );
				break;

			case 4:
				transport->Send( "Intermediate" );
				break;

			default:
				sprintf( temp, "%ld", format->type );
				transport->Send( temp );
				break;
		};
		transport->Send( "'>\n" );

		field_change* ftemp = change_info.ds;
		int counter = 0;
		while (ftemp) {
			if (ftemp->change_type != CHANGE_NONE) {
				buffer_field* fld;
				int changed = ftemp->change_type;
				if (changed & CHANGE_DELETED) {
					buffer_ds* temp = old_ds;
					for (int i=0; i < ftemp->number; i++ )
						temp++;
					fld = (buffer_field*)olddb->FixOffset( temp->field_offset );
				} else {
					while (counter < ftemp->number ) {
						counter++;
						ds++;
					};
					fld = (buffer_field*)FixOffset( ds->field_offset );
				};

				transport->Send( "\t\t<Field Mode='" );
				if (changed & CHANGE_DELETED) {
//					printf( "\tField deleted: %s (%d)\n", fld->name, fld->number );
					transport->Send( "Delete" );
				} else if (changed & CHANGE_CREATED) {
//					printf( "\tField created: %s (%d)\n", fld->name, fld->number );
					transport->Send( "Create" );
				} else {
//					printf( "\tField changed: %s (%d)\n", fld->name, fld->number );
					transport->Send( "Change" );
				};

				assert( fld );
				sprintf( temp, "' Name='%s' Number='%ld'", fld->name, fld->number );
				transport->Send( temp );

				if (changed & CHANGE_CREATED)
					changed |= CHANGE_TYPE | CHANGE_LENGTH | CHANGE_DECPLACES;

				if ((changed & (CHANGE_DELETED)) == 0) {
					transport->Send( ">\n" );

					if (changed & CHANGE_TYPE) {
						sprintf( temp, "\t\t\t<Prop Name='Datatype'>%ld</Prop>\n", fld->type );
						transport->Send( temp );
					};

					if (changed & CHANGE_LENGTH) {
						sprintf( temp, "\t\t\t<Prop Name='Length'>%ld</Prop>\n", fld->length );
						transport->Send( temp );
					};

					if (changed & CHANGE_NAME) {
						sprintf( temp, "\t\t\t<Prop Name='Name'>%s</Prop>\n", fld->name );
						transport->Send( temp );
					};

					if (changed & CHANGE_FLAGS) {
						sprintf( temp, "\t\t\t<Prop Name='Flags'>%ld</Prop>\n", fld->flags );
						transport->Send( temp );
					};

					if (changed & CHANGE_DECPLACES) {
						sprintf( temp, "\t\t\t<Prop Name='DecPlaces'>%ld</Prop>\n", fld->decimal_places );
						transport->Send( temp );
					};

					transport->Send( "\t\t</Field>\n" );
				} else
					transport->Send( "/>\n" );
			};

			ftemp = ftemp->next;
		};
		transport->Send( "\t</Format>\n" );
	};
};


//
// Analyze changes in formats
//
void XCPrepInterface::AnalyzeFormats( XCFileTransport* transport, XCPrepInterface* olddb ) {
	// Start with finding new/changed buffers
	format_change change_info;
	change_info.new_buffer = (buffer_format*)FixOffset( appdic_header->formats.offset );
	for (int i=0; i<appdic_header->formats.count; i++) {
		// Skip system formats
		if (!IsSystemBuffer( change_info.new_buffer->number )) {
			// Clear out the change information
			change_info.type = CHANGE_NONE;
			change_info.ds = NULL;
			change_info.last = NULL;

			// Check for format descriptor record in old definition
			change_info.old_buffer = olddb->GetFormatInfo( change_info.new_buffer->number );

			if (change_info.old_buffer) {
				// Next analyze the data structures for this buffer
				AnalyzeFormatFields( change_info, olddb );
			} else {
				change_info.type = CHANGE_CREATED;
				for (int i=0; i<change_info.new_buffer->ds_count; i++)
					change_info.AddFieldChange( new field_change( CHANGE_CREATED, i ) );
			};

			OutputFormatChange( transport, change_info );
		};				
		change_info.new_buffer++;
	};

	// Next check for deleted buffers
	memset( &change_info, 0, sizeof( format_change ) );
	change_info.old_buffer = (buffer_format*)olddb->FixOffset( olddb->appdic_header->formats.offset );
	for (int i=0; i<olddb->appdic_header->formats.count; i++) {
		change_info.new_buffer = GetFormatInfo( change_info.old_buffer->number );
		if (!change_info.new_buffer)
			change_info.type = CHANGE_DELETED;
		else
			change_info.type = CHANGE_NONE;

		OutputFormatChange( transport, change_info );

		change_info.old_buffer++;
	};
};



//
// Analyze changes in a formats data structures
//
void XCPrepInterface::AnalyzeFormatFields( format_change& change_info, XCPrepInterface* olddb ) {
	XCBuffer *newbuf = NULL, *oldbuf = NULL;
	XCField *new_field = NULL, *old_field = NULL;

	INFUNC( XCPrepInterface::AnalyzeFormatFields, this )

	// Create the actual buffer and use it to check for changes
	int format = change_info.new_buffer->number;
	newbuf = CreateBuffer( format, true );
	oldbuf = olddb->CreateBuffer( format, true );

	for (int i=0; i<newbuf->GetFieldCount(); i++) {
		new_field = newbuf->GetFieldByIndex( i );
		old_field = oldbuf->GetField( new_field->GetFieldNumber() );

		if (!old_field) {
//			printf( "\tField %s (%d) was added\n", new_field->GetFieldName(), new_field->GetFieldNumber() );
			change_info.AddFieldChange( new field_change( CHANGE_CREATED, i ) );
		} else {
			// Check for changes in properties
			long changed = CHANGE_NONE;
			if (new_field->GetType() != old_field->GetType())
				changed |= CHANGE_TYPE;
			if (new_field->GetLength() != old_field->GetLength())
				changed |= CHANGE_LENGTH;
			if (strcmp( new_field->GetFieldName(), old_field->GetFieldName()) != 0 )
				changed |= CHANGE_NAME;
			if ( (new_field->GetFlags() & FLAG_ALLOW_NULL) != (old_field->GetFlags() & FLAG_ALLOW_NULL) )
				changed |= CHANGE_FLAGS;

			if (changed) {
				change_info.AddFieldChange( new field_change( changed, i ) );
//				printf( "\tField %s (%d) was changed\n", new_field->GetFieldName(), new_field->GetFieldNumber() );
			};
		};
	};

	for (int i=0; i<oldbuf->GetFieldCount(); i++) {
		old_field = oldbuf->GetFieldByIndex( i );
		new_field = newbuf->GetField( old_field->GetFieldNumber() );

		if (!new_field) {
//			printf( "\tField %s (%d) was dropped\n", old_field->GetFieldName(), old_field->GetFieldNumber() );
			change_info.AddFieldChange( new field_change( CHANGE_DELETED, i ) );
		};
	};

	newbuf->Release();
	oldbuf->Release();

	OUTFUNC();
};




//	Write APPDIC data structures
//
//////////////////////////////////////////////////////////////////////////////


// Writes the data areas for buffer/data_structures/fields
void XCPrepInterface::LoadPrepInfo( XCTxn* dbtxn ) {
	INFUNC( XCPrepInterface::LoadPrepInfo, this );

	datasize = sizeof(buffer_header);

	// Allocate temporary space
	new_data = new char[ 10240 * 1024 ];

	// Clear out the header
	header = (buffer_header*)new_data;
	memset( header, 0, sizeof( buffer_header ) );
	memcpy( header, "EA10", 4 );

	// Load application information (required for unicode multiplier)
	if (!myopts.appdic_first_run || appl_number != 1)
		LoadAppInfo( dbtxn );
	else
		current_app.unicode_mult = 1;

	// Load buffer information
	cout << "Load data definitions..." << endl;
	LoadBufferInfo( dbtxn );

	// Load database information
	cout << "Load database definitions..." << endl;
	LoadDatabaseInfo( dbtxn );

	cout << "Load misc definitions..." << endl;
	if (!myopts.appdic_first_run || appl_number != 1) {
	//	LoadScreenInfo( dbtxn );
		LoadTransactionInfo( dbtxn );
		LoadUserExitInfo( dbtxn );
		LoadMiscInfo( dbtxn );
	};

	WritePrepInfo( true );

//	MoveCount++;

	OUTFUNC();
};




void XCPrepInterface::WritePrepInfo( bool use_new_data ) {
	char *tmpfile;

	tmpfile = (char*)malloc( MAX_PATH );

	// Write everything to a file
	sprintf( tmpfile, "%s%s%s%s%d.Appdic.New", appdic.root, argvpath, OSFILEDELIM, "APPDIC", appl_number );

	XCFileTransport output( this );
	output.Open( tmpfile, write_only, create_always );

	if (use_new_data) {
//		header->crc32 = MyCRC.Get_CRC32( (unsigned char*)(new_data + sizeof( buffer_header )), datasize - sizeof( buffer_header ) );
//		header->checksum = MyCRC.Get_Checksum( (unsigned char*)(new_data + sizeof( buffer_header )), datasize - sizeof( buffer_header ) );
		output.Send( new_data, datasize );
	} else {
//		header->crc32 = MyCRC.Get_CRC32( (unsigned char*)(APPDIC_DATA + sizeof( buffer_header )), APPDIC_DATA_SIZE - sizeof( buffer_header ) );
//		header->checksum = MyCRC.Get_Checksum( (unsigned char*)(APPDIC_DATA + sizeof( buffer_header )), APPDIC_DATA_SIZE - sizeof( buffer_header ) );
		output.Send( APPDIC_DATA, APPDIC_DATA_SIZE );
	};
	
	output.Close();

	if (use_new_data) {
		MoveIt[ MoveCount ].filename = "APPDIC";
		MoveIt[ MoveCount ].nowhere = false;
//		cout << "Adding move it: " << MoveCount << ", " << MoveIt[ MoveCount ].filename << endl;
		MoveCount++;
	};


	free( tmpfile );


	if (use_new_data) {
		buffer_format* format = (buffer_format*)&new_data[ header->formats.offset ];
		for (int i=0; i<header->formats.count; i++) {
			if (format->dd_offset) {
				buffer_db_tables* table = (buffer_db_tables*)(new_data + format->dd_offset);
				ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Format %s:%d", format->name, table->db_offset );
			};

			format++;
		};
	};
};


// Find field record offset
buffer_field* XCPrepInterface::GetLocalFieldInfo( int field_number ) {
	INFUNC( XCPrepInterface::GetLocalFieldInfo, this );

	buffer_field* buf_field = (buffer_field*)&new_data[ header->fields.offset ]; 
	int count = header->fields.count;
	while (count && buf_field->number != field_number) {
		buf_field++;
		count--;
	};

	if (buf_field->number != field_number) {
		cout << "Field undefined: " << field_number << endl;
		assert( false );
		RAISETOOLSERROR( 1 );
	};

	OUTFUNCRET( buf_field );
};


// Find literal field record offset
buffer_field* XCPrepInterface::GetLocalLiteral( buffer_format* lit_buf, int lit_num ) {
	INFUNC( XCPrepInterface::GetLocalLiteral, this );

	buffer_ds* ds = (buffer_ds*)&new_data[ lit_buf->ds_offset ];
	int count = lit_buf->ds_count;

	buffer_field* field = (buffer_field*)&new_data[ ds->field_offset ];
	while (count && field->number != lit_num) {
		count--;
//		printf( "Count = %d\n", count );
		ds++;
		field = (buffer_field*)&new_data[ ds->field_offset ];
	};

	// Didn't find it
	if (!count) {
		cout << "Didn't find literal field!" << endl;
		RAISETOOLSERROR( 1 );
	};

//	cout << "FOUND LITERAL!!!  " << field->name << "  value='" << field->defaultval << "'" << endl;
	OUTFUNCRET( field );
};



buffer_format* XCPrepInterface::GetLocalFormatInfo( int format_number ) {
	INFUNC( XCPrepInterface::GetLocalFormatInfo, this );

	buffer_format* buf_format = (buffer_format*)&new_data[ header->formats.offset ];
	int count = header->formats.count;
	while (count && format_number != buf_format->number) {
		buf_format++;
		count--;
	};

	// Abort if we can't find a format to index to
	if (!count) {
//		cout << "Can't find format: " << format_number << endl;
		RAISETOOLSERROR_1PARAM( ERR_INV_FORMAT_NUMBER, format_number );
	};

	OUTFUNCRET( buf_format );
};





/*
buffer_screen_defn* XCPrepInterface::GetLocalScreenInfo( long format_number, long term_type ) {
	INFUNC( XCPrepInterface::GetLocalScreenInfo, this );

	buffer_screen_defn* scr = (buffer_screen_defn*)&new_data[ header->screens.offset ];
	long buf = (char*)GetLocalFormatInfo( format_number ) - new_data;

	for (int count=0; count < header->screens.count; count++) {
		if (buf == scr->format_offset && term_type == scr->term_type)
			RETURNFUNC( scr );
		scr++;
	};

//	cout << "Can't find screen: " << format_number << endl;
	RAISETOOLSERROR( 1 );

	OUTFUNCRET( NULL );
};
*/






void XCPrepInterface::SetAllocSize( buffer_ds* buf_ds, buffer_format* buf_format ) {
	// Ensure that we have a unicode multiplier
	assert( current_app.unicode_mult );

	switch( buf_ds->data_type ) {
		//
		// Automatically set size of the following field types
		//
		case XCBOOL:
			// Always one character
			buf_ds->max_length = 1;
			buf_ds->alloc_size = 2;
			break;

		case S_INTEGER:
			buf_ds->alloc_size = buf_ds->max_length = sizeof( short int );
			break;

		case LITERAL_VAL:
		case D_INTEGER:
		case AUTONUMBER:
			buf_ds->alloc_size = buf_ds->max_length = sizeof( long );
			break;

		case DEC_FLOAT:
			buf_ds->alloc_size = buf_ds->max_length = sizeof( float );
			break;

		case DEC_DOUBLE:
			buf_ds->alloc_size = buf_ds->max_length = sizeof( double );
			break;

		case TIME_24HR_DB:
			buf_ds->max_length = 8;
			buf_ds->alloc_size = 9;
			break;

		case DATE_DB:
			buf_ds->max_length = 10;
			buf_ds->alloc_size = 11;
			break;

		case DATE_TIMESTAMP:
			buf_ds->max_length = 19;
			buf_ds->alloc_size = 20;
			break;

		case VAR_STRING:
			// TODO: Not sure why this fixed seg. faults.  Please revisit.
			buf_ds->alloc_size = ((buf_ds->max_length + 1) * current_app.unicode_mult) + 2;
			break;

		case XCBLOB:
			buf_ds->alloc_size = sizeof(long int *) + 4;		// + 4 for the lenght
			break;

		default:
			// We have a string type of some sort, do we need to use unicode multiplier?
			if ((GetDataTypeDefn( buf_ds->data_type )->type_group & TYPE_STRING) == TYPE_STRING ) {
				buf_ds->alloc_size = (buf_ds->max_length + 1) * current_app.unicode_mult;
			} else {
				// Otherwise, just add 1 to the max length size (assume string value)
				buf_ds->alloc_size = buf_ds->max_length + 1;
			};
			break;
	};

	// Align the allocation size on correct boundary
	buf_ds->alloc_size = PrepAlign( buf_ds->alloc_size );
};


//
// Params:
//	defaultval	Pointer to buffer dataspace where default should go
//	ds			Data structure record
//	field		Field record
//	buf_literal	?
//
void XCPrepInterface::FillDefaultValue( char* defaultval, buffer_ds* ds, buffer_field* field, buffer_format* format, buffer_format* buf_literal ) {
	INFUNC( XCPrepInterface::FillDefaultValue, this );

	long lit_result;

	switch( ds->data_type ) {
		case S_INTEGER: {
			short int* val = (short int*)defaultval;
			*val = atoi( field->defaultval );
			break;
		};

		case D_INTEGER:
		case AUTONUMBER: {
			long* val = (long*)defaultval;
			*val = atoi( field->defaultval );
			break;
		};

		case LITERAL_STRING:
		case LITERAL_VAL: {
			if (myopts.appdic_first_run)
				break;

			assert( buf_literal );
			if (!buf_literal)
				RAISETOOLSERROR( 1 );

			// Find the literal group
			int count = header->litgroups.count;
			lit_group* group = (lit_group*)&new_data[ header->litgroups.offset ];
			while (count && group->number != ds->dec_places) {
				group++;
				count--;
			};
			if (!count) {
				cout << "Literal group not found for field " << field->name << " in buffer " << format->name << "!" << endl;
				RAISETOOLSERROR_1PARAM( ERR_INVALID_LITERAL_GROUP, ds->dec_places );
			};

//			printf( "New search:  group number = %d  [%s]\n", ds->dec_places, group->name );
			// Search the literals in the group to try and find our value
			count = group->literals.count;
			if (strlen( field->defaultval )) {
				// The field is non-blank, so find the literal to it matches...
				lit_relate* relate = (lit_relate*)&new_data[ group->literals.offset ];
				buffer_field* temp = GetLocalLiteral( buf_literal, relate->lit_number );
				char* litval = temp->defaultval;
				while (count && strcmp( litval, field->defaultval ) != 0) {
//					printf( "Compare Lit#%d '%s' to '%s'\n", relate->lit_number, litval, field->defaultval );
					count--;
					relate++;
					if (count) {
						litval = GetLocalLiteral( buf_literal, relate->lit_number )->defaultval;
					};
				};
				if (count)
					lit_result = relate->value;
			} else if (ds->flags & 512) {
				// The default value is blank, only allow if the field has the null flag set
				lit_result = NOT_IN_LIT_GROUP;
			} else
				count = 0;

			if (!count)
				RAISETOOLSERROR_3PARAM( ERR_INVALID_LITERAL_DEFAULT, field->name, format->name, field->defaultval );

			if (ds->data_type == LITERAL_VAL) {
				// Literal value
				long* val = (long*)defaultval;
				*val = lit_result;
			} else {
				// Literal string
				memcpy( defaultval, field->defaultval, ds->alloc_size );
			};
			break;
		};

		case DEC_DOUBLE: {
			double* val = (double*)defaultval;
			*val = atof( field->defaultval );
			break;
		};

		case DEC_FLOAT: {
			float* val = (float*)defaultval;
			*val = (float)atof( field->defaultval );
			break;
		};

		default:
			// Move the default value into the buffer
			memcpy( defaultval, field->defaultval, ds->alloc_size );
			break;
	};

	OUTFUNC();
};



// Reset all the change flags within DATA_STRUCTURES
void XCPrepInterface::ResetChangeFlags( XCTxn* txn ) {
	INFUNC( XCPrepIntreface::ResetChangeFlags, this );

	XCSQLGen query( this );
	XCCompare comp;

	XCBuffer* DS = NULL;
	XCBuffer* DSCopy = NULL;

	try {
		DS = CreateBuffer( FMT_DATA_STRUCTURES, false );
		DSCopy = DS->Copy();

		DS->GetField( FLD_APPDIC_VERSION )->SetLong( 0 );
		DSCopy->GetField( FLD_APPDIC_VERSION )->SetLong( 1 );

		query.MakeUPDATE( DS, DSCopy );
		query.AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		txn->ExecuteSQL( &query, SQL_NO_RESULT );
	} catch( ... ) {
		DS->Release();
		DSCopy->Release();
		throw;
	};
	DS->Release();
	DSCopy->Release();

	OUTFUNC();
};




void XCPrepInterface::LoadAppInfo( XCTxn* dbtxn ) {
	INFUNC( XCPrepInterface::LoadAppInfo, this );

	XCDBStatement* stmt;
	appl_defn* ad;

	XCCompare comp;

	XCBuffer* bApplDefn = CreateBuffer( FMT_APPL_DEFN, false );

	// Verify that we have an application defn record for the app (needed for unicode
	// multiplier)
	query->MakeSELECT( bApplDefn );

	int appl_number = app_info->GetField( FLD_APPL_NBR )->GetLong( true );
	if (appl_number != 1 && appl_number != 2)
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );

	stmt = dbtxn->ExecuteSQL( query );
	if (stmt->Eof()) {
		stmt->Release();
		RAISETOOLSERROR( ERR_BAD_APP_NUMBER );
	};

	// Stash away the data
	header->appl_defn.offset = datasize;
	while (!stmt->Eof()) {
		ad = (appl_defn*)&new_data[ datasize ];
		memset( ad, 0, sizeof( appl_defn ) );
		bApplDefn->GetField( FLD_APPL_NAME )->GetString( ad->appl_name );
		ad->appdic_version = bApplDefn->GetField( FLD_APPDIC_VERSION )->GetLong();
		ad->appl_nbr = bApplDefn->GetField( FLD_APPL_NBR )->GetShort();
		ad->unicode_mult = bApplDefn->GetField( FLD_UNICODE_MULT )->GetLong( true );

		if (bApplDefn->GetField( FLD_APPL_NBR )->GetLong( true ) == appl_number) {
			current_app = *ad;
		};

		datasize += sizeof( appl_defn );
		header->appl_defn.count++;

		stmt->MoveNext();
	};

	bApplDefn->Release();
	stmt->Release();

	OUTFUNC();
};




buffer_format* XCPrepInterface::CreateSystemBuffers( buffer_format* buf_format ) {
	// Setup system values format
	memset( buf_format, 0, sizeof( buffer_format ) );
	buf_format->number = NEW_SYSTEM_VALUES;
	strcpy( buf_format->name, "SYSTEM_VALUES" );
	strcpy( buf_format->real_name, "SYSTEM_VALUES" );
	buf_format->type = 4;
	buf_format++;
	datasize += sizeof( buffer_format );
	header->formats.count++;

	// Setup literal defn format
	for (int i=0; i<MAX_LANGUAGES; i++) {
		memset( buf_format, 0, sizeof( buffer_format ) );
		buf_format->number = NEW_LITERAL_DEFN + i;
		sprintf( buf_format->name, "LITERAL_%3ld", i + NEW_LITERAL_DEFN );
		strcpy( buf_format->real_name, buf_format->name );
		buf_format->type = 4;
		buf_format++;
		datasize += sizeof( buffer_format );
		header->formats.count++;
	};

	memset( buf_format, 0, sizeof( buffer_format ) );

	return buf_format;
};




void XCPrepInterface::LoadBufferInfo( XCTxn* dbtxn ) {
	INFUNC( XCPrepInterface:::LoadBufferInfo, this );

	XCOrder orderlist[2];

CHECKMEM;

	XCDBStatement*	stmt;
	XCBuffer*		bDS_desc = CreateBuffer( FMT_DATA_STRUCTURES, false );
	XCBuffer*		bField_desc = CreateBuffer( FMT_FIELD_DESCRIPTOR, false );
	XCBuffer*		bFormat_desc = CreateBuffer( FMT_FORMAT_DESCRIPTR, false );

	buffer_format*	buf_format;
	buffer_ds*		buf_ds;
	buffer_field*	buf_field;

	bool AddedSystem = myopts.appdic_first_run;
	buffer_format* sysstart = NULL;

	XCCompare comp;
	XCOrder order;
	int count = 0;

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading FORMAT_DESCRIPTR..." );
	header->formats.offset = datasize;

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bFormat_desc, FLD_FORMAT_NUMBER, "FORMAT_DESCRIPTR" ) << " FORMAT_DESCRIPTR records..." << endl;

	// Select all format descriptr records for this application
	query->MakeSELECT( bFormat_desc );
	query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
	query->AddORDERBY( FLD_FORMAT_NUMBER, &order );
	stmt = dbtxn->ExecuteSQL( query );

	// Loop thru result set
	buf_format = (buffer_format*)&new_data[ datasize ];
	while (!stmt->Eof()) {
		memset( buf_format, 0, sizeof( buffer_format ) );

		long format_number = bFormat_desc->GetField( FLD_FORMAT_NUMBER )->GetLong();

		// Don't allow buffer's with reserved numbers
		if (IsSystemBuffer( format_number ))
			RAISETOOLSERROR( 1 );

		// Check to see if we need to add special system buffers
		if (format_number > NEW_SYSTEM_VALUES && !AddedSystem) {
			sysstart = buf_format;
			buf_format = CreateSystemBuffers( buf_format );
			AddedSystem = true;
		};


		// Add the buffer format
		buf_format->number = format_number;
		bFormat_desc->GetField( FLD_FORMAT_NAME )->GetString( buf_format->name );
		bFormat_desc->GetField( FLD_FORMAT_NAME )->GetString( buf_format->real_name );
		buf_format->type = bFormat_desc->GetField( FLD_FORMAT_TYPE )->GetShort();
		buf_format->child_format = bFormat_desc->GetField( FLD_CHILD_FORMAT )->GetLong();
		buf_format->indexed = false;


		if (!SkipDatabase || buf_format->type >= 3) {
//			cout << "New format: " << buf_format->name << "  number=" << buf_format->number <<  endl;
			header->formats.count++;
			datasize += sizeof( buffer_format );
			buf_format++;
		};

		stmt->MoveNext();
	};
	stmt->Release();

	// Check to see if we need to add special system buffers
	if (!AddedSystem) {
		sysstart = buf_format;
		buf_format = CreateSystemBuffers( buf_format );
		AddedSystem = true;
	};

	// Set the offset to field_descr records
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading FIELD_DESCRIPTOR..." );
	header->fields.offset = datasize;

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bField_desc, FLD_FIELD_NUMBER, "FIELD_DESCRIPTOR" ) << " FIELD_DESCRIPTOR records..." << endl;

	// Select all field_desc records
	query->MakeSELECT( bField_desc );
	query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
	query->AddORDERBY( FLD_FIELD_NUMBER, orderlist );
	stmt = dbtxn->ExecuteSQL( query );
	while (!stmt->Eof()) {
		buf_field = (buffer_field*)&new_data[ datasize ];
		memset( buf_field, 0, sizeof( buffer_field ) );

		buf_field->number = bField_desc->GetField( FLD_FIELD_NUMBER )->GetLong();
		bField_desc->GetField( FLD_FIELD_NAME )->GetString( buf_field->name );
		buf_field->type = bField_desc->GetField( FLD_DATA_TYPE )->GetShort();
		buf_field->length = bField_desc->GetField( FLD_DB_BYTE_LENGTH )->GetLong( true );
		buf_field->decimal_places = bField_desc->GetField( FLD_DECIMAL_PLACES )->GetShort();
		buf_field->flags = bField_desc->GetField( FLD_DESCRIPTOR_FLAGS )->GetLong();

		XCField* tempfield = bField_desc->GetField( FLD_DESC_MESSAGE );
		if (tempfield)
			buf_field->desc_message = tempfield->GetLong( true );
		else
			buf_field->desc_message = 0;

		bField_desc->GetField( FLD_FIELD_VALUE )->GetString( buf_field->defaultval );

		int sugg_type = 0;
		// Change to DATE_DB
		if ( ( buf_field->type >= 9 && buf_field->type <= 19 ) || ( buf_field->type >= 31 && buf_field->type <= 39 ) )
		{
			sugg_type = 42;
		}
		// Change to TIME_24HR_DB
		else if ( buf_field->type >= 20 && buf_field->type <= 25 )
		{
			sugg_type = 41;
		}
		// Change to DUR_HH_TIME
		else if ( buf_field->type == 26 )
		{
			sugg_type = 27;
		}
		// Change to DUR_HM_TIME
		else if ( buf_field->type == 28 )
		{
			sugg_type = 29;
		}
		if ( sugg_type )
		{
			printf( "FIELD ERROR: INVALID TYPE: %s is currently unsupported type %d, suggest change to type %d\n", buf_field->name, buf_field->type, sugg_type );
			RAISETOOLSERROR( ERR_DATA_TYPE );
		}

		header->fields.count++;
		datasize += sizeof( buffer_field );

		stmt->MoveNext();
	};
	stmt->Release();

	
	
	
	
	// Set the offset to data structure records
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading DATA_STRUCTURES..." );
	header->ds.offset = datasize;

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bDS_desc, 0, "DATA_STRUCTURES" ) << " DATA_STRUCTURES records..." << endl;

	// Select all data structure records
	static long ds_order_list[3] = { FLD_FORMAT_NUMBER, FLD_SEQUENCE_NBR, 0 };

	query->MakeSELECT( bDS_desc );
	query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
	query->AddORDERBY( ds_order_list, orderlist );
	stmt = dbtxn->ExecuteSQL( query );

	buf_format = (buffer_format*)&new_data[ header->formats.offset ];
	count = header->formats.count;

	buf_ds = (buffer_ds*)&new_data[ datasize ];
	while (!stmt->Eof()) {
		memset( buf_ds, 0, sizeof( buffer_ds ) );

		int format_number = bDS_desc->GetField( FLD_FORMAT_NUMBER )->GetLong();
		try {
			buf_format = GetLocalFormatInfo( format_number );
			buf_format->ds_count++;

			buf_ds->field_offset = bDS_desc->GetField( FLD_FIELD_NUMBER )->GetLong();
			buf_ds->min_length = bDS_desc->GetField( FLD_MIN_LENGTH )->GetLong( true );
			buf_ds->max_length = bDS_desc->GetField( FLD_MAX_LENGTH )->GetLong( true );
			buf_ds->data_type = bDS_desc->GetField( FLD_DATA_TYPE )->GetShort();
			buf_ds->dec_places = bDS_desc->GetField( FLD_DECIMAL_PLACES )->GetShort();
			buf_ds->flags = bDS_desc->GetField( FLD_DESCRIPTOR_FLAGS )->GetLong();
			buf_ds->index = -1;

			// Choose the correct size for the data
			SetAllocSize( buf_ds, buf_format );

			int sugg_type = 0;
			// Change to DATE_DB
			if ( ( buf_field->type >= 9 && buf_field->type <= 19 ) || ( buf_field->type >= 31 && buf_field->type <= 39 ) )
			{
				sugg_type = 42;
			}
			// Change to TIME_24HR_DB
			else if ( buf_field->type >= 20 && buf_field->type <= 25 )
			{
				sugg_type = 41;
			}
			// Change to DUR_HH_TIME
			else if ( buf_field->type == 26 )
			{
				sugg_type = 27;
			}
			// Change to DUR_HM_TIME
			else if ( buf_field->type == 28 )
			{
				sugg_type = 29;
			}
			if ( sugg_type )
			{			
				printf( "BUFFER ERROR: INVALID TYPE: %s has field %d that is currently unsupported type %d, suggest change to type %d\n", buf_format->real_name, buf_ds->field_offset, buf_ds->data_type, sugg_type );
				RAISETOOLSERROR( ERR_DATA_TYPE );
			}

			header->ds.count++;
			datasize += sizeof( buffer_ds );
			buf_ds++;
		} catch( ... ) {
			if (!SkipDatabase)
				throw;
		};

		stmt->MoveNext();
	};
	stmt->Release();



	// Make sure we get system values and literals
	buffer_format* buf_literal = NULL;
	if (AddedSystem && !myopts.appdic_first_run) {
		LoadSystemValueInfo( dbtxn, sysstart );
		sysstart++;
		LoadLiteralInfo( dbtxn, sysstart );
		buf_literal = GetLocalFormatInfo( NEW_LITERAL_DEFN );
	};

	// Now read what we wrote and index everything...
	cout << "- Indexing..." << endl;
	buf_ds = (buffer_ds*)&new_data[ header->ds.offset ];
	buf_format = (buffer_format*)&new_data[ header->formats.offset ];

	for (int i = 0; i < header->formats.count; i++) {
		if (!IsSystemBuffer( buf_format->number )) {
			int size = 0;

			buf_format->ds_offset = (char*)buf_ds - new_data;
			buf_format->default_offset = datasize;

//			printf( "Format: %-16.16s, field count = %ld\n", buf_format->name, buf_format->ds_count );

			char* defaultval = new_data + datasize;

			for (int j = 0; j < buf_format->ds_count; j++) {
				buf_ds->data_offset = size;

				// Find field record offset
				buf_field = GetLocalFieldInfo( buf_ds->field_offset );
				buf_ds->field_offset = (char*)buf_field - new_data;

				// Insert the default value
				FillDefaultValue( defaultval, buf_ds, buf_field, buf_format, buf_literal );
/*
				printf( "\tField: %-16.16s = '%s', size=%3ld, alloc=%3ld, offset=%d\n",
					buf_field->name,
					buf_field->defaultval,
					buf_ds->max_length,
					buf_ds->alloc_size,
					size );
*/
				// Update buffer size
				defaultval += buf_ds->alloc_size;
				size += buf_ds->alloc_size;

				buf_ds++;
			};

			buf_format->length = size;
	//		cout << "Buffer: " << buf_format->name << "  " << buf_format->length << endl;

			datasize += size;
		};

		// Now index the buffer
		IndexBuffer( buf_format );

		// Move on to the next record
		buf_format++;
	};


	bFormat_desc->Release();
	bField_desc->Release();
	bDS_desc->Release();

	OUTFUNC();
};




//
// IndexBuffer
//
// Creates an index of the field records.  Indexes according to field number to provide
// optimizations for GetField and MoveCorresponding calls.
//
void XCPrepInterface::IndexBuffer( buffer_format* format ) {
	typedef struct index_type {
		int fld_nbr;
		buffer_ds* ds;
	};

//	printf( "INDEX BUFFER: %s\n", format->name );

	// Create the index array
	index_type* index = new index_type[ format->ds_count ];

	// Fill the index array
	buffer_ds* buf_ds = (buffer_ds*)&new_data[ format->ds_offset ];
	for (int i=0; i<format->ds_count; i++) {
		buffer_field* buf_field = (buffer_field*)&new_data[ buf_ds->field_offset ];
//		printf( "\tFIELD:%s\n", buf_field->name );
		index[i].fld_nbr = buf_field->number;
		index[i].ds = buf_ds;
		buf_ds++;
	};

	// Sort the index array (bubble sort) - method doesn't really matter
	for( int i=0; i<format->ds_count; i++)
	{
		// Start at the first element, and sort to the end
		for(int j=1; j<(format->ds_count-i); j++)
		{
			// If they are out of order, rearrange
			if( index[j-1].fld_nbr > index[j].fld_nbr ) {
				// Swap the elements
				index_type temp = index[j];
				index[j] = index[j-1];
				index[j-1] = temp;
			};
		}
	};

	// Fill the ds array with index numbers
	for (int i=0; i<format->ds_count; i++)
		index[i].ds->index = i;

	// Debug output
	buf_ds = (buffer_ds*)&new_data[ format->ds_offset ];
	for (int i=0; i<format->ds_count; i++) {
		buffer_field* buf_field = (buffer_field*)&new_data[ buf_ds->field_offset ];
//		printf( "i = %d,  fld_nbr = %d, name=%s, index=%d\n", i, buf_field->number, buf_field->name, buf_ds->index );
		buf_ds++;
	};

	// Mark the format as indexed
	format->indexed = true;

	// Cleanup
	delete [] index;
};



void XCPrepInterface::LoadDatabaseInfo( XCTxn* dbtxn ) {
	INFUNC( XCPrepInterface::LoadDatabaseInfo, this );

	XCBuffer* bDB_defn = CreateBuffer( FMT_DATABASE_DEFN, false );
	XCBuffer* bDBTable_defn = CreateBuffer( FMT_DATASET_DESCR, false );
	buffer_db_defn* buf_db_defn;
	buffer_db_tables* buf_db_tables;
	buffer_db_relate* buf_db_relate;
	XCCompare comp;
	XCOrder order;
	XCDBStatement* stmt;

	long list[5];
	XCOrder orderlist[4];

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading DATABASE_DEFN..." );
	header->databases.offset = datasize;

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bDB_defn, 0, "DATABASE_DEFN" ) << " DATABASE_DEFN records..." << endl;

	// Select all format descriptr records for this application
	query->MakeSELECT( bDB_defn );
	query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
	query->AddORDERBY( FLD_DATABASE_NBR, &order );
	stmt = dbtxn->ExecuteSQL( query );

	// Loop thru result set
	buf_db_defn = (buffer_db_defn*)&new_data[ datasize ];
	while (!stmt->Eof()) {
		memset( buf_db_defn, 0, sizeof( buffer_db_defn ) );

		buf_db_defn->number = bDB_defn->GetField( FLD_DATABASE_NBR )->GetLong( true );
		bDB_defn->GetField( FLD_DATABASE_NAME )->GetString( buf_db_defn->name );
		bDB_defn->GetField( FLD_DB_USER )->GetString( buf_db_defn->user );
		bDB_defn->GetField( FLD_DB_PASSWORD )->GetString( buf_db_defn->pass );
		buf_db_defn->type = bDB_defn->GetField( FLD_DB_TYPE )->GetLong( true );

		header->databases.count++;
		datasize += sizeof( buffer_db_defn );
		buf_db_defn++;
		stmt->MoveNext();
	};
	stmt->Release();




	if (!SkipDatabase) {
		// Dataset descriptors
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading DATASET_DESCR..." );
		header->db_tables.offset = datasize;

		// Show number of records to loop through...
		cout << "- Processing " << GetCount( query, dbtxn, bDBTable_defn, 0, "DATASET_DESCR" ) << " DATASET_DESCR records..." << endl;

		// Select all format descriptr records for this application
		list[0] = FLD_DATABASE_NBR;
		list[1] = FLD_FORMAT_NUMBER;
		list[2] = 0;

		query->MakeSELECT( bDBTable_defn );
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		query->AddORDERBY( list, orderlist );
		stmt = dbtxn->ExecuteSQL( query );

		// Loop thru result set
		buf_db_tables = (buffer_db_tables*)&new_data[ datasize ];
		while (!stmt->Eof()) {
			memset( buf_db_tables, 0, sizeof( buffer_db_tables ) );

			// Link table to a database
			int count=0;
			long dbnum = bDBTable_defn->GetField( FLD_DATABASE_NBR )->GetShort();
			buffer_db_defn* db = (buffer_db_defn*)&new_data[ header->databases.offset ];
			while (db->number != dbnum && count < header->databases.count) {
				count++;
				db++;
			};
			if (count == header->databases.count)
				RAISETOOLSERROR(1);

			buf_db_tables->db_offset = (char*)db - new_data;

			// Link database to this table
			db->table_count++;
			if (db->table_offset == 0)
				db->table_offset = (char*)buf_db_tables - new_data;

			// Link table to a format
			int format_number = bDBTable_defn->GetField( FLD_FORMAT_NUMBER )->GetLong();
			buffer_format* temp;
			try {
				temp = GetLocalFormatInfo( format_number );
			} catch( XCToolsError& error ) {
				error.Clear();
				RAISETOOLSERROR_1PARAM( ERR_DD_NO_FORMAT, format_number );
			};

			buf_db_tables->format = (char*)temp - new_data;

			// Debug
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "DB Table: %s in %d", temp->name, db );

			// Link format to this table
			temp->dd_offset = (char*)buf_db_tables - new_data;

			// Update other table stuff
			buf_db_tables->view_flag = bDBTable_defn->GetField( FLD_VIEW_FLAG )->GetShort();

			// Update header/size information
			header->db_tables.count++;
			datasize += sizeof( buffer_db_tables );
			buf_db_tables++;
			stmt->MoveNext();
		};
		stmt->Release();




		// Dataset key definitions
		if (!bootstrap) {
			XCBuffer* bDBRel_defn = CreateBuffer( FMT_DB_RELATE, false );
			ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading DB_RELATE..." );
			header->db_relate.offset = datasize;

			// Show number of records to loop through...
			cout << "- Processing " << GetCount( query, dbtxn, bDBRel_defn, 0, "DB_RELATE" ) << " DB_RELATE records..." << endl;

			// Select all format descriptr records for this application
			list[0] = FLD_DATABASE_NBR;
			list[1] = FLD_FORMAT_NUMBER;
			list[2] = FLD_RELATE_TYPE;
			list[3] = FLD_FIELD_NUMBER;
			list[4] = 0;

			query->MakeSELECT( bDBRel_defn );
			query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
			query->AddORDERBY( list, orderlist );
			stmt = dbtxn->ExecuteSQL( query );

			// Loop thru result set
			buf_db_relate = (buffer_db_relate*)&new_data[ datasize ];
			while (!stmt->Eof()) {
				memset( buf_db_relate, 0, sizeof( buffer_db_relate ) );

				// Link relate to field descriptor
				int field_number = bDBRel_defn->GetField( FLD_FIELD_NUMBER )->GetLong();
				buffer_field* temp = GetLocalFieldInfo( field_number );
				buf_db_relate->field_offset = (char*)temp - new_data;

				// Link relate to a table record
				int format = bDBRel_defn->GetField( FLD_FORMAT_NUMBER )->GetLong();
				buffer_format* tempf = GetLocalFormatInfo( format );

				// Ensure that the supplied buffer is a database table type
				if (!tempf || tempf->type >= 3)
					RAISETOOLSERROR_1PARAM( ERR_DB_RELATE_NO_FORMAT, tempf->type );

				buffer_db_tables* tempt = (buffer_db_tables*)(new_data + tempf->dd_offset);

				// Link relate to target format, if we have one
				XCField* tgt_format = bDBRel_defn->GetField( FLD_TGT_FORMAT );
				if (tgt_format) {
					format = tgt_format->GetLong();
					if (format) {
						tempf = GetLocalFormatInfo( format );
						buf_db_relate->format_offset = (char*)tempf - new_data;
					} else
						buf_db_relate->format_offset = 0;
				};

				// Link relate to target field, if we have one
				XCField* tgt_field = bDBRel_defn->GetField( FLD_TGT_FIELD );
				if (tgt_field) {
					field_number = tgt_field->GetLong();
					if (field_number) {
						temp = GetLocalFieldInfo( field_number );
						buf_db_relate->field_tgt_offset = (char*)temp - new_data;
					} else
						buf_db_relate->field_tgt_offset = 0;
				};

				if (!tempt->relate_count)
					tempt->relate_offset = datasize;
				tempt->relate_count++;

				// Update extra info
				buf_db_relate->type = bDBRel_defn->GetField( FLD_RELATE_TYPE )->GetLong();

	//			cout << "Add relate record: " << buf_db_relate->type << "  " << temp->name << "  " << tempf->name << " Count = " << tempt->relate_count << endl;

				header->db_relate.count++;
				datasize += sizeof( buffer_db_relate );
				buf_db_relate++;
				stmt->MoveNext();
			}; // while
			stmt->Release();
			bDBRel_defn->Release();
		}; // if !bootstrap
	};		// skip database


	// Cleanup
	bDB_defn->Release();
	bDBTable_defn->Release();

	OUTFUNC();
};







void XCPrepInterface::LoadUserExitInfo( XCTxn* dbtxn ) {
	XCDBStatement*	stmt = NULL;
	XCBuffer*		bUE_Defn = CreateBuffer( FMT_USER_EXIT_DEFNS, false );

	XCOrder			order;
	XCCompare		comp;
	user_exit_defn*	defn;

	header->user_exits.offset = datasize;
	header->user_exits.count = 0;

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bUE_Defn, 0, "USER_EXIT_DEFNS" ) << " USER_EXIT_DEFNS records..." << endl;

	// Select all format descriptr records for this application
	query->MakeSELECT( bUE_Defn );
	query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
	query->AddORDERBY( FLD_USER_EXIT_NUMBER, &order );
	stmt = dbtxn->ExecuteSQL( query );

	defn = (user_exit_defn*)&new_data[ datasize ];
	while (!stmt->Eof()) {
		memset( defn, 0, sizeof( user_exit_defn ) );

		defn->number = bUE_Defn->GetField( FLD_USER_EXIT_NUMBER )->GetLong( true );
		defn->active = (bUE_Defn->GetField( FLD_USER_EXIT_ACTIVE )->GetLong( true ) > 0);

		header->user_exits.count++;
		datasize += sizeof( user_exit_defn );
		defn++;
		stmt->MoveNext();
	};

	CleanupObjects( 2, &stmt, &bUE_Defn );
};



void XCPrepInterface::LoadTransactionInfo( XCTxn* dbtxn ) {
};





void XCPrepInterface::LoadLiteralInfo( XCTxn* dbtxn, buffer_format* format ) {
	INFUNC( XCPrepInterface::LoadLiteralInfo, this );

	XCBuffer* bLiteral = NULL;
	XCBuffer* bLiteral_values = NULL;
	XCDBStatement* stmt = NULL;
	XCDBStatement* stmt2 = NULL;
	XCCompare comp;
	XCOrder order[3];

	try {
		// Load Literal Defn info
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading LITERAL_DEFN..." );
		header->literals.offset = datasize;

		// Create the literal defn buffers
		bLiteral = CreateBuffer( FMT_LITERAL_DEFN, false );
		bLiteral_values = CreateBuffer( FMT_LITERAL_VALUES, false );

		// Show number of records to loop through...
		cout << "- Processing " << GetCount( query, dbtxn, bLiteral, 0, "LITERAL_DEFN" ) << " LITERAL_DEFN records..." << endl;

		// Select all records for this application
		query->MakeSELECT( bLiteral );
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		query->AddORDERBY( FLD_LITERAL_NUMBER, order );
		stmt = dbtxn->ExecuteSQL( query );

		int defaults_size = 0;
		while (!stmt->Eof()) {
			format->ds_count++;
			defaults_size += PrepAlign( bLiteral->GetField( FLD_LITERAL_LENGTH )->GetLong( true ) + 1 );
			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;

		// Set the information for all the other literal_defn buffers
		for (int i=0; i<MAX_LANGUAGES; i++) {
			format[i].ds_count = format[0].ds_count;
			format[i].length = defaults_size;
		};

		// Allocate room and make pointers to things
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Literals size: %d, %d fields", defaults_size, format->ds_count );

		// Data Structures - different for all the literal_defn buffers
		buffer_ds* lit_ds[ MAX_LANGUAGES ];
		for (int i=0; i<MAX_LANGUAGES; i++) {
			lit_ds[i] = (buffer_ds*)&new_data[ datasize ];
			format[i].ds_offset = datasize;
			format[i].indexed = false;
			datasize += (sizeof( buffer_ds ) * format->ds_count);
		};

		// Fields - common to all literal_defn buffers
		buffer_field* field;
		buffer_field* save_field;
		save_field = field = (buffer_field*)&new_data[ datasize ];
		datasize += (sizeof( buffer_field ) * format->ds_count);

		// Defaults - different for all the literal_defn buffers
		char* defaults[ MAX_LANGUAGES ];
		for (int i=0; i<MAX_LANGUAGES; i++) {
			defaults[i] = &new_data[ datasize ];
			format[i].default_offset = datasize;
			datasize += defaults_size;
		};

		stmt = dbtxn->ExecuteSQL( query );

/* TURN ON APP DEBUGGING
		XCAppOptions ap;
		GetOptions( ap );
		ap.debug_type_filter = TRC_DATABASE;
		SetOptions( ap );
*/
		// Now query the value records
		long st2_fields[3] = { FLD_LITERAL_NUMBER, FLD_LANG_ID, 0 };
		query->MakeSELECT( bLiteral_values );
		query->AddWHERE( 1, app_info->GetField( FLD_APPL_NBR ) );
		query->AddORDERBY( st2_fields, order );
		stmt2 = dbtxn->ExecuteSQL( query );


		// Go fill in data!
		long offset = 0;
		while (!stmt->Eof()) {
			// Grab the default value
			long size = bLiteral->GetField( FLD_LITERAL_LENGTH )->GetLong( true );
			long allocsize = PrepAlign( size + 1 );

			// Create the data-structure record for each literal buffer
			for (int i=0; i<MAX_LANGUAGES; i++) {
				memset( lit_ds[i], 0, sizeof( buffer_ds ) );
				lit_ds[i]->alloc_size = allocsize;
				lit_ds[i]->data_offset = offset;
				lit_ds[i]->data_type = LITERAL_STRING;
				lit_ds[i]->field_offset = (char*)field - new_data;
				lit_ds[i]->max_length = size;
				lit_ds[i]->index = -1;
				lit_ds[i]++;
			};

			// Prepare the literal field record
			memset( field, 0, sizeof( buffer_field ) );
			bLiteral->GetField( FLD_LITERAL_NAME )->GetString( field->name );
			field->number = bLiteral->GetField( FLD_LITERAL_NUMBER )->GetLong( true );

			// Create the defaults
			for (int i=0; i<MAX_LANGUAGES; i++) {
				// Clear out the field
				memset( defaults[i], 0, allocsize );

				// Do we have a language record for this literal?  If not, leave blank
				while (!stmt2->Eof() && bLiteral_values->GetField( FLD_LITERAL_NUMBER )->GetLong( true ) < field->number) {
					printf( "Looking for literal number %ld\n", field->number );
					stmt2->MoveNext();
				};

				if (!stmt2->Eof()) {
					if (bLiteral_values->GetField( FLD_LANG_ID )->GetLong( true ) == i) {
						// Is this the correct literal number (in case one is missing)
						if (bLiteral_values->GetField( FLD_LITERAL_NUMBER )->GetLong( true ) == field->number) {
							//printf( "number: %d lang: %d  value: %s\n", field->number, bLiteral_values->GetField( FLD_LANG_ID )->GetLong( true ), bLiteral_values->GetField( FLD_LITERAL )->GetStringPtr() );
							bLiteral_values->GetField( FLD_LITERAL )->GetString( defaults[i] );

							// The field should get the default value of the base language
							if (i==0)
								bLiteral_values->GetField( FLD_LITERAL )->GetString( field->defaultval );

							stmt2->MoveNext();
						} else {
							// Blank out the default value
							*defaults[i] = 0;
							if (i==0)
								*field->defaultval = 0;
						};
					} else {
//						printf( "NO LITERAL DEFINITION FOR LANGUAGE!\n" );
					};
				};

				defaults[i] += allocsize;
			};
			field++;

			offset += allocsize;

			header->literals.count++;

			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;

		stmt2->Release();
		stmt2 = NULL;


		// Create the literal groups now
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading LIT_GROUPS..." );
		header->litgroups.offset = datasize;

		bLiteral->Release();
		bLiteral = CreateBuffer( FMT_LIT_GROUP, false );

		// Show number of records to loop through...
		cout << "- Processing " << GetCount( query, dbtxn, bLiteral, 0, "LIT_GROUP" ) << " LIT_GROUP records..." << endl;

		// Select all records for this application
		query->MakeSELECT( bLiteral );
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		query->AddORDERBY( FLD_LIT_GROUP_NBR, order );
		stmt = dbtxn->ExecuteSQL( query );

		lit_group* group = (lit_group*)&new_data[ datasize ];
		while (!stmt->Eof()) {
			memset( group, 0, sizeof( lit_group ) );

			bLiteral->GetField( FLD_LIT_GROUP_NAME )->GetString( group->name );
			group->number = bLiteral->GetField( FLD_LIT_GROUP_NBR )->GetLong( true );

			datasize += sizeof( lit_group );
			group++;

			header->litgroups.count++;
			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;





		// Create the literal relations now
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading LIT_RELATE..." );
		header->litrelate.offset = datasize;

		bLiteral->Release();
		bLiteral = CreateBuffer( FMT_LIT_RELATE, false );

		// Show number of records to loop through...
		cout << "- Processing " << GetCount( query, dbtxn, bLiteral, 0, "LIT_RELATE" ) << " LIT_RELATE records..." << endl;

		// Select all records for this application
		long list[3];
		list[0] = FLD_LIT_GROUP_NBR;
		list[1] = FLD_LIT_GROUP_VAL;
		list[2] = 0;

		query->MakeSELECT( bLiteral );
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		query->AddORDERBY( list, order );
		stmt = dbtxn->ExecuteSQL( query );

		int count = 0;
		group = (lit_group*)&new_data[ header->litgroups.offset ] - 1;
		lit_relate* relate = (lit_relate*)&new_data[ datasize ];
		while (!stmt->Eof()) {
			memset( relate, 0, sizeof( lit_relate ) );

			int gnumber = bLiteral->GetField( FLD_LIT_GROUP_NBR )->GetLong( true );
			while (count < header->litgroups.count && group->number != gnumber) {
				group++;
				count++;

//				cout << "LIT GROUP: " << group->name << endl;
				group->literals.offset = datasize;
			};

			relate->lit_number = bLiteral->GetField( FLD_LITERAL_NUMBER )->GetLong( true );
			relate->value = bLiteral->GetField( FLD_LIT_GROUP_VAL )->GetLong( true );

			int fcount = 0;
			buffer_field* temp = save_field;
			while (fcount < header->literals.count && temp->number != relate->lit_number) {
				temp++;
				fcount++;
			};

//			cout << "\tLiteral: " << temp->name << " = " << relate->value << endl;

			datasize += sizeof( lit_relate );
			relate++;

			group->literals.count++;
			header->litrelate.count++;
			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;
	} catch( ... ) {
		if (bLiteral) bLiteral->Release();
		if (bLiteral_values) bLiteral_values->Release();
		if (stmt) stmt->Release();
		if (stmt2) stmt2->Release();
		throw;
	};
	bLiteral->Release();
	bLiteral_values->Release();

	OUTFUNC();
};



void XCPrepInterface::LoadSystemValueInfo( XCTxn* dbtxn, buffer_format* format ) {
	INFUNC( XCPrepInterface::LoadSystemValueInfo, this );

	XCBuffer* bSysVal = NULL;
	XCDBStatement* stmt = NULL;
	XCCompare comp;
	XCOrder order[2];

	// Load the system values info
	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading SYSTEM_VALUES..." );
	header->sysval.offset = datasize;

	assert( datasize > 0 );

	try {
		bSysVal = CreateBuffer( FMT_SYSTEM_VALUES, false );

		// Show number of records to loop through...
		cout << "- Processing " << GetCount( query, dbtxn, bSysVal, 0, "SYSTEM_VALUES" ) << " SYSTEM_VALUES records..." << endl;

		// Select all records for this application
		query->MakeSELECT( bSysVal );
		query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
		query->AddORDERBY( FLD_FIELD_NUMBER, order );
		stmt = dbtxn->ExecuteSQL( query );

		// Count the number of records
		while (!stmt->Eof()) {
			format->ds_count++;
			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;

		// Allocate room and make pointers to things
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Sys val %d fields", format->ds_count );

		// data_structures
		buffer_ds* ds = (buffer_ds*)&new_data[ datasize ];
		format->ds_offset = datasize;
		datasize += (sizeof( buffer_ds ) * format->ds_count);

		// fields
		buffer_field* field = (buffer_field*)&new_data[ datasize ];
		datasize += (sizeof( buffer_field ) * format->ds_count);

		// defaults
		char* defaults = &new_data[ datasize ];
		format->default_offset = datasize;

		stmt = dbtxn->ExecuteSQL( query );

		long offset = 0;
		while (!stmt->Eof()) {
			// Create a field structure record
			memset( field, 0, sizeof( buffer_field ) );
			bSysVal->GetField( FLD_FIELD_NAME )->GetString( field->name );
			bSysVal->GetField( FLD_FIELD_VALUE )->GetString( field->defaultval );
			field->number = bSysVal->GetField( FLD_FIELD_NUMBER )->GetLong( true );

			// Create a data structure record
			memset( ds, 0, sizeof( buffer_ds ) );
			ds->data_offset = offset;
			ds->data_type = bSysVal->GetField( FLD_DATA_TYPE )->GetLong( true );
			ds->field_offset = (char*)field - new_data;

			// Calculate the field size, default to string length
			ds->max_length = strlen( field->defaultval );
			ds->alloc_size = 0;
			SetAllocSize( ds, format );

			// Grab the default value
			FillDefaultValue( defaults, ds, field, format );
			defaults += ds->alloc_size;

			// Move on to next record
			offset += ds->alloc_size;
			field++;
			ds++;

			stmt->MoveNext();
		};
		stmt->Release();
		stmt = NULL;

		format->length = offset;
		format->indexed = false;
		datasize += offset;
	} catch( ... ) {
		if (bSysVal) bSysVal->Release();
		if (stmt) stmt->Release();
		throw;
	};

	bSysVal->Release();

	OUTFUNC();
};




void XCPrepInterface::LoadMiscInfo( XCTxn* dbtxn ) {
	INFUNC( XCPrepInterface::LoadDatabaseInfo, this );

	language_defn* lang;
	buffer_msg_defn msg;

	XCBuffer* bMsgDefn = CreateBuffer( FMT_MSG_DEFN, false );
	XCBuffer* bMsgMaster = CreateBuffer( FMT_MSG_MASTER, false );
	XCBuffer* bLangDefn = CreateBuffer( FMT_LANGUAGE_DEFN, false );

	XCCompare comp[2];
	XCOrder order[2];
	long order_list[2];
	XCDBStatement* stmt;
	XCFileTransport output( this );

	// Write everything to a file
	char* tmpfile = (char*)malloc( MAX_PATH );
	sprintf( tmpfile, "%s%s%s%s%d.Appdic.New", appdic.root, argvpath, OSFILEDELIM, "MESSAGES", appl_number );
	output.Open( tmpfile, write_only, create_always );
	free( tmpfile );

	ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reading MSG_DEFN...  Offset=%d", datasize );
	header->messages.offset = datasize;

	// Determine the size of the message index
	int message_count = GetCount( query, dbtxn, bMsgMaster, 0, "MSG_MASTER" );
	bMsgMaster->Release();
	buffer_msg_hdr* message_index = new buffer_msg_hdr[ message_count+1 ];
	message_index[0].number = 0;
	message_index[0].index = 0;
	output.Send( message_index, sizeof( buffer_msg_hdr ) * (message_count+1));

	// Show number of records to loop through...
	cout << "- Processing " << GetCount( query, dbtxn, bMsgDefn, 0, "MSG_DEFN" ) << " MSG_DEFN records..." << endl;

	// Select all format descriptr records for this application
	XCField* list[3];

	list[0] = bMsgDefn->GetField( FLD_APPL_NBR );
	list[0]->SetLong( 0 );
	list[1] = app_info->GetField( FLD_APPL_NBR );
	list[2] = NULL;

	comp[1].logical = logic_or;

	query->MakeSELECT( bMsgDefn );
	query->AddWHERE( list, comp );

	order_list[0] = FLD_MSG_NBR;
	order_list[1] = FLD_LANG_ID;
	query->AddORDERBY( order_list, order, 2 );
	stmt = dbtxn->ExecuteSQL( query );

	// Loop thru result set
	int counter = 0;
	int last_number = 0;
	while (!stmt->Eof()) {
		memset( &msg, 0, sizeof( buffer_msg_defn ) );

		msg.number = bMsgDefn->GetField( FLD_MSG_NBR )->GetLong( true );
		XCField* temp = bMsgDefn->GetField( FLD_LANG_ID );
		if (temp)
			msg.lang_id = temp->GetLong( true );
		else
			msg.lang_id = 0;
		bMsgDefn->GetField( FLD_MSG_TEXT )->GetString( msg.message );

		// If message number != last_number, inc count
		if (msg.number != last_number) {
			if (msg.lang_id)
				printf( "WARNING: NO DEFAULT LANGUAGE MESSAGE FOR #%d\n", msg.number );
			last_number = msg.number;
			counter++;

			if (counter <= message_count) {
				message_index[counter].index = output.GetPos();
				message_index[counter].number = last_number;
			} else
				printf( "MESSAGE INTEGRITY ISSUES!!!!\n" );
		};

		// Write to the file
		output.Send( &msg, sizeof( buffer_msg_defn ) );

		stmt->MoveNext();
	};
	stmt->Release();

	// Rewrite the message index
	if (counter != message_count)
		printf( "INTEGRITY ERROR!\n" );
	message_index[0].number = counter;
	output.Seek( 0 );
	output.Send( message_index, sizeof( buffer_msg_hdr ) * (message_count+1));
	output.Close();
	delete [] message_index;

	//
	// Read the language definitions
	//

	// Show number of records to loop through...
//	cout << "- Processing " << GetCount( query, dbtxn, bLangDefn, 0, "LANGUAGE_DEFN" ) << " LANGUAGE_DEFN records..." << endl;

	query->MakeSELECT( bLangDefn );
	query->AddORDERBY( FLD_LANG_ID, order );
	stmt = dbtxn->ExecuteSQL( query );

	header->languages.offset = datasize;
	header->languages.count = 0;

	lang = (language_defn*)&new_data[ datasize ];

	// Loop thru result set
	while (!stmt->Eof()) {
		memset( lang, 0, sizeof( language_defn ) );

		lang->id = bLangDefn->GetField( FLD_LANG_ID )->GetLong( true );
		bLangDefn->GetField( FLD_LANG_NAME )->GetString( lang->name );
		bLangDefn->GetField( FLD_LANG_DESC )->GetString( lang->desc );

		lang++;
		header->languages.count++;
		datasize += sizeof( language_defn );
		stmt->MoveNext();
	};
	stmt->Release();

	//
	// Cleanup
	//
	bMsgDefn->Release();
	bLangDefn->Release();

	MoveIt[ MoveCount ].filename = "MESSAGES";
	MoveIt[ MoveCount ].nowhere = false;
	MoveCount++;

	OUTFUNC();
};



void XCPrepInterface::write_Appdic_File( XCTxn* dbtxn, long buftype, char* appl, bool nowhere )
{
	INFUNC( write_Appdic_File, this )

	fstream output;
//	char tempstr[ 257 ];

	XCDBStatement* dbstmt;
	XCBuffer* buf = appdic.CreateBuffer( buftype, false );


	// Attempt to read all information 
	try {
CHECKMEM;
		query->MakeSELECT( buf );
CHECKMEM;
		if (!nowhere)
		{
			XCCompare comp;
			comp.comparison = cmp_equals;
			query->AddWHERE( app_info->GetField( FLD_APPL_NBR ), &comp );
CHECKMEM;
		};

		dbstmt = dbtxn->ExecuteSQL( query );
CHECKMEM;
	} catch( ... ) {
		buf->Release();
		throw;
	};

	if (dbstmt) {
		output.open( appl, ios::out | ios::binary );
		while (!dbstmt->Eof()) {
			// handle each record type
			switch( buftype ) {
				case FMT_TRANSACTION_DEFN: {
					transaction_defn record5;
					memset( &record5, 0, sizeof( transaction_defn ) );
					buf->GetField( FLD_TRANS_CODE )->GetString( record5.trans_code );
					record5.trans_number = buf->GetField( FLD_TRANS_NUMBER)->GetShort();
					record5.use_defaults = buf->GetField( FLD_DEFAULT_REC_FLAG)->GetShort() == 1;
					output.write( (char*)&record5, sizeof( transaction_defn ) );
					break;
				};

/*
				case FMT_TRANSACTIONS: {
					transactions record7;
					memset( &record7, 0, sizeof( transactions ) );
					record7.trans_number = buf->GetField(FLD_TRANS_NUMBER)->GetShort();
					record7.format_number = buf->GetField(FLD_FORMAT_NUMBER)->GetLong();
					record7.txn_trigger = buf->GetField(FLD_TXN_TRIGGER)->GetShort();
					record7.txn_order = buf->GetField(FLD_TXN_ORDER)->GetShort();
		
					output.write( (char*)&record7, sizeof( transactions ) );
					break;
				};
*/
				case FMT_APPL_DEFN: {
					appl_defn record12;
					memset( &record12, 0, sizeof( appl_defn ) );

					buf->GetField( FLD_APPL_NAME )->GetString( record12.appl_name );
//					record12.appdic_version = buf->GetField( FLD_APPDIC_VERSION )->GetLong();
					record12.appl_nbr = buf->GetField( FLD_APPL_NBR )->GetShort();
//					record12.unicode_mult = buf->GetField( FLD_UNICODE_MULT )->GetLong( true );

					output.write( (char*)&record12, sizeof( appl_defn ) );
					break;
				};

				default:
					RAISETOOLSERROR( 123415 );
					break;
			}
			dbstmt->MoveNext();
		};
		output.close();
	} else
		cout << "Unable to open statement handle" << endl;


	dbstmt->Release();
	buf->Release();

	OUTFUNC()
};



void XCPrepInterface::WriteStructure( int format_number, char* filename, XCTxn* dbtxn, bool nowhere ) {
	INFUNC( WriteStructure, this )

	char tmpfile[ MAX_PATH ];

	// Append application number
	if (nowhere)
		sprintf( tmpfile, "%s%s%s%s.Appdic.New", appdic.root, argvpath, OSFILEDELIM, filename );
	else
	{
		int app_number = app_info->GetField( FLD_APPL_NBR )->GetShort();
		sprintf( tmpfile, "%s%s%s%s%d.Appdic.New", appdic.root, argvpath, OSFILEDELIM, filename, app_number );
	}

CHECKMEM;
	write_Appdic_File( dbtxn, format_number, tmpfile, nowhere );
CHECKMEM;

	// Prepare the move it count
	MoveIt[ MoveCount ].filename = filename;
	MoveIt[ MoveCount ].nowhere = nowhere;
//	cout << "Adding move it: " << MoveCount << ", " << MoveIt[ MoveCount ].filename << endl;

	MoveCount++;

	OUTFUNC()
};


void XCPrepInterface::MoveOldFiles( bool remove_only ) {

	INFUNC( MoveOldFiles, this )

	int i;

	char* tmpfile;
	char* oldfile;
	char* newfile;
	char* bakfile;

	tmpfile = new char[ MAX_PATH ];
	oldfile = new char[ MAX_PATH ];
	newfile = new char[ MAX_PATH ];
	bakfile = new char[ MAX_PATH ];

	sprintf( tmpfile, "%s%s%s", appdic.root, argvpath, OSFILEDELIM );

	for (i = 0; i<MoveCount; i++) {

		if ( MoveIt[ i ].nowhere )
			sprintf( oldfile, "%s%s.Appdic", tmpfile, MoveIt[ i ].filename );
		else
			sprintf( oldfile, "%s%s%d.Appdic", tmpfile, MoveIt[ i ].filename, appl_number);

		strcpy( newfile, oldfile );
		strcpy( bakfile, oldfile );
		
		// Append the file extensions
		strcat( newfile, ".New" );
		strcat( bakfile, ".Bak" );

		try {
			if (!remove_only) {
				// Delete previous backup files
				XCDeleteFile( bakfile );

				// Rename current file to backup file
				XCRenameFile( oldfile, bakfile );

				// Rename new file to current files
				XCRenameFile( newfile, oldfile );
			} else {
				XCDeleteFile( newfile );
			};
		} catch( ... ) {
			delete [] tmpfile;
			delete [] oldfile;
			delete [] newfile;
			delete [] bakfile;
			throw;
		};
	};

	delete [] tmpfile;
	delete [] oldfile;
	delete [] newfile;
	delete [] bakfile;

	OUTFUNC()
};




void XCPrepInterface::MakeLiteralsCreateField( MakeLiteralsInfo& info, const char* name, int type, int fld_number ) {
	int number = info.format->ds_count++;
	buffer_ds* ds = &info.ds[number];
	buffer_field* fld = new buffer_field;
    info.fld[number] = fld;

	// Create the data-structures record
	ds->field_offset = (long)((char*)fld - APPDIC_DATA);
	ds->data_offset = info.format->length;
	ds->min_length = 0;
	ds->dec_places = 0;
	ds->flags = 0;
	ds->severity_code = 0;
	ds->data_type = type;

	switch( type ) {
		case D_INTEGER:
			ds->max_length = sizeof( long );
			ds->alloc_size = sizeof( long );
			break;

		case STRING:
			ds->max_length = 16;
			ds->alloc_size = 20;
			break;
	};
	info.format->length += ds->alloc_size;

	// Create the field record
	fld->number = fld_number;
	strcpy( fld->name, name );
	fld->type = type;
	fld->decimal_places = 0;
	fld->flags = 0;

	switch( type ) {
		case D_INTEGER:
			strcpy( fld->defaultval, "0" );
			fld->length = sizeof( long );
			break;

		case STRING:
			*fld->defaultval = 0;
			fld->length = 16;
			break;
	};
};



void XCPrepInterface::MakeLiterals( XCTxn* dbtxn, XCTxn* app_txn ) {
	XCBuffer* report_literals = NULL;
	XCBuffer* lit_groups = NULL;
	XCBuffer* lit_relate = NULL;
	XCBuffer* lit_values = NULL;
	XCDBStatement* stmt1 = NULL;
	char* defaults = NULL;
	XCOrder order;
	MakeLiteralsInfo info;

	// Zero out all information
	memset( &info, 0, sizeof( MakeLiteralsInfo ) );
	info.ds = new buffer_ds[4];
	info.format = new buffer_format;

	try {
		// Fill in the format record
		memset( info.format, 0, sizeof( buffer_format ) );
		strcpy( info.format->name, "REPORT_LITERALS" );
		info.format->ds_offset = (long)((char*)info.ds - APPDIC_DATA);

		// Create the field and ds records
		MakeLiteralsCreateField( info, "LIT_GROUP_NBR", D_INTEGER, FLD_LIT_GROUP_NBR );
		MakeLiteralsCreateField( info, "LIT_GROUP_VAL", D_INTEGER, FLD_LIT_GROUP_VAL );
		MakeLiteralsCreateField( info, "LANG_ID", D_INTEGER, FLD_LANG_ID );
		MakeLiteralsCreateField( info, "LITERAL", STRING, FLD_LITERAL );

		// Now create the defaults for the format
		defaults = new char[ info.format->length ];
		memset( defaults, 0, info.format->length );
		info.format->default_offset = (long)( defaults - APPDIC_DATA );

		// Create the format table record
		info.format->dd_offset = (long)((char*)&info.table - APPDIC_DATA);
		memset( &info.table, 0, sizeof( buffer_db_tables ) );
		info.table.db_offset = appdic_header->databases.offset;	// Use the first database listed
		info.table.format = (long)((char*)info.format - APPDIC_DATA);
		info.table.view_flag = -1;

		// Create the dynamic buffer
		report_literals = CreateDynamicBuffer( info.format );

		// Drop any existing REPORT_LITERALS table
		query->MakeDROP( report_literals );
		try {
			dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
		} catch( XCToolsException& error ) {
			// Ignore messages for now - ignore only doesn't exist error
			error.Clear();
		};

		// Create a new REPORT_LITERALS table
		query->MakeCREATE( report_literals );
		dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//
		// Move over the literal values
		//
		lit_groups = appdic.CreateBuffer( FMT_LIT_GROUP );
		lit_relate = appdic.CreateBuffer( FMT_LIT_RELATE );
		lit_values = appdic.CreateBuffer( FMT_LITERAL_VALUES );

		lit_groups->MoveCorresponding( app_info, NULL );

		// Join lit_relate table on appl_nbr and lit_group_nbr
		XCJoin join1;
		join1.buffer = lit_relate;
		join1.type = join_inner;
		join1.predicate = new XCCompare[2];
		join1.predicate_count = 2;
		join1.predicate[0].field = lit_groups->GetField( FLD_APPL_NBR );
		join1.predicate[0].field2 = lit_relate->GetField( FLD_APPL_NBR );
		join1.predicate[1].field = lit_groups->GetField( FLD_LIT_GROUP_NBR );
		join1.predicate[1].field2 = lit_relate->GetField( FLD_LIT_GROUP_NBR );

		// Join lit_values table on appl_nbr and literal_number
		XCJoin join2;
		join2.buffer = lit_values;
		join2.predicate = new XCCompare[2];
		join2.predicate_count = 2;
		join2.predicate[0].field = lit_groups->GetField( FLD_APPL_NBR );
		join2.predicate[0].field2 = lit_values->GetField( FLD_APPL_NBR );
		join2.predicate[1].field = lit_relate->GetField( FLD_LITERAL_NUMBER );
		join2.predicate[1].field2 = lit_values->GetField( FLD_LITERAL_NUMBER );

		// Create the query
		appdic.query->MakeSELECTByNum( lit_groups, 2, FLD_APPL_NBR, FLD_LIT_GROUP_NBR );
		appdic.query->AddJOIN( &join1 );
		appdic.query->AddJOINByNum( &join2 );
		appdic.query->AddWHERE( 1, lit_groups->GetField( FLD_APPL_NBR ) );

		// For each literal group:
		stmt1 = app_txn->ExecuteSQL( appdic.query );
		while (!stmt1->Eof()) {
			// Move returned values into the dynamic buffer
			report_literals->MoveCorresponding( lit_relate, NULL );
			report_literals->MoveCorresponding( lit_values, NULL );

			// Write the record
			query->MakeINSERT( report_literals );
			dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

			// Move on to the next record
			stmt1->MoveNext();
		};
	} catch( ... ) {
		CleanupObjects( 5, &report_literals, &lit_groups, &lit_relate, &lit_values, &stmt1 );
		if (defaults)
			delete [] defaults;
		throw;
	};
	CleanupObjects( 5, &report_literals, &lit_groups, &lit_relate, &lit_values, &stmt1 );
	if (defaults)
		delete [] defaults;
};




bool CheckLockFile() {
	sprintf( lockfilename, "%sprep.lock", appdic.ToolsDir );

    XCFileTransport lockfile( NULL );
	try {
		lockfile.Open( lockfilename, read_only, create_new );
	} catch( XCToolsError& err ) {
		err.Clear();
		return false;
	};
	lockfile.Close();
	return true;
};


int main(int argc, char* argv[]) {

	Protea::ShowIntro( "prep" );

	if (argc > 1 && !strcmp(argv[1],"-L"))
	{
		Protea::ShowLicense();
		return 0;
	};

	// Initialize Protea
	Protea::Initialize();

	int resultcode = 0;

	//
	// Set program options
	//
	try {
		INFUNC( main, NULL )

		appdic.GetOptions( myopts );
		myopts.appdic_first_run = false;
		myopts.no_transactions = true;

		appl_number = -1;
		int count = 1;
		while (count < argc) {
			char* arg = argv[count];

			if (*arg == '/') {
				switch( arg[1] ) {
					case 's':
						SkipDatabase = true;
						break;

					case 'f':
						myopts.appdic_first_run = true;
						break;

					case 'a':
						AnalysisOnly = true;
						break;

					////// Undocumented flags //////
					case 'n':
						nomove = true;
						break;

					case 'b':
						bootstrap = true;
						myopts.appdic_first_run = true;
						break;
				};
			} else
				appl_number = atoi( arg );
			count++;
		};

		if (appl_number < 0) {
			cout << "Usage: prep [/s] [/f] [/a] [/c] <APPL_NBR>" << endl;
			cout << "  /s	Skip databases" << endl;
			cout << "  /f	First run" << endl;
			cout << "  /a	Analysis only" << endl;
			return 1;
		}

		if (SkipDatabase && appl_number == 1)
		{
			cout << "I can't let you do that!" << endl;
			return 1;
		};

		argvpath = new char[14];
		sprintf( argvpath, "Tools%sappdic", OSFILEDELIM );

		// Load the appdic information
		cout << "Initialize appdic" << endl;

		for ( int i = 0; i < num_tc; i++ )
			myopts.debug_level_cutoff[ i ] = 0xFF;
		myopts.debug_type_filter = 0xFFFF;
		myopts.debug_type_filter = TRC_DATABASE;
		myopts.debug_type_filter = TRC_DATABASE | TRC_APPINTERFACE;
//		myopts.debug_type_filter = TRC_DATABASE | TRC_APPINTERFACE | TRC_FUNCTIONS;
//		myopts.debug_type_filter = TRC_FUNCTIONS;
		myopts.debug_type_filter = 0;

		appdic.Initialize( "Prep", 1 );
		appdic.ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Initialized!" );
/*
		// Check the lock file
		if (!CheckLockFile()) {
			cout << "Prep currently in use, try again later." << endl;
			return 2;
		};
*/

		// Load our signal handler
		OSDEP_SIGNAL( SIGINT, &sighandler )

		// Connect to the database and create transaction
		XCTxn* dbtxn = appdic.GetDatabase()->CreateTxn();

		// Set our application number
		static const long fieldlist[3] = {FLD_APPL_NBR, FLD_APPL_NAME, 0 };
		app_info = appdic.CreateDynamicBuffer( fieldlist, false );
		app_info->GetField( FLD_APPL_NBR )->SetShort( appl_number );

		try {
			appdic.LoadPrepInfo( dbtxn );

			// If this is the first time we are running, then reinit here.
			if (myopts.appdic_first_run && appl_number == 1) {
				// Unload data structures dependent on current appdic structure
				appdic.CleanupObjects( 1, &app_info );
				dbtxn->EndTxn( true );

				appdic.ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Reinitialize" );
				appdic.Initialize( argv[0], appl_number, ".New" );
				appdic.ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Fix database table names" );
				appdic.FirstCheck();
				if (!AnalysisOnly) {
					if (bootstrap) {
						// If bootstrapping
						appdic.CleanupObjects( 1, &app_info );
						try {
							app_info = appdic.CreateDynamicBuffer( fieldlist, false );
							app_info->GetField( FLD_APPL_NBR )->SetShort( appl_number );

							dbtxn = appdic.GetDatabase()->CreateTxn();
						} catch( ... ) {
							dbtxn->EndTxn( false );
							throw;
						};
						dbtxn->EndTxn( true );
					};

					// Write the updated information out
					appdic.WritePrepInfo( false );
				};

				// Re-init and recreate buffers, transactions
				appdic.Initialize( argv[0], appl_number, ".New" );
				dbtxn = appdic.GetDatabase()->CreateTxn();

				app_info = appdic.CreateDynamicBuffer( fieldlist, false );
				app_info->GetField( FLD_APPL_NBR )->SetShort( appl_number );
			};

			appdic.WriteStructure( FMT_TRANSACTION_DEFN, "Transaction_Defn", dbtxn );

			// Application independent files
			appdic.WriteStructure( FMT_APPL_DEFN, "Appl_Defn", dbtxn, true );

			// End writing files
		} catch( ... ) {
			dbtxn->EndTxn( false );
			throw;
		};

		// Commit txn and close database
		dbtxn->EndTxn( true );

		appdic.GetDatabase()->Refresh();

		if (!myopts.appdic_first_run) {
			newdb = new XCPrepInterface();
			newdb->SetOptions( myopts );
			newdb->Initialize( argv[0], appl_number, ".New" );
			newdb->SetCustomizerApp();

			olddb = new XCPrepInterface();
			myopts.no_database = true;
			olddb->SetOptions( myopts );
			olddb->Initialize( argv[0], appl_number );
			olddb->SetCustomizerApp();

			// Perform analysis
			newdb->Analysis( olddb );
			try {
				// Perform database changes
				newdb->CheckTables( olddb );

				if (!AnalysisOnly) {
					newdb->GetDatabase()->Refresh();
//					olddb->GetDatabase()->Refresh();

					// Update database information
					newdb->AffectChanges( true, olddb, &appdic );

					// Write appdic information
					newdb->WritePrepInfo( false );
				};
			} catch( ... ) {
				if (!AnalysisOnly) 
					newdb->AffectChanges( false, olddb, &appdic );
				throw;
			};
		};

		if (!AnalysisOnly) {
			if (!myopts.appdic_first_run)
			{
				newdb->MakeUEInfo();
			};

			appdic.MoveOldFiles( nomove );
		} else
			appdic.MoveOldFiles( true );
		OUTFUNC()

	} catch( XCToolsException& error ) {
		if (appdic.root)
			appdic.MoveOldFiles( true );
		XCOutputCallStack( &appdic, error.GetErrorNumber(),error.GetDescription() );
		resultcode = error.GetErrorNumber();
	} catch( ... ) {
		if (appdic.root)
			appdic.MoveOldFiles( true );
		XCOutputCallStack( &appdic, -1, NULL );
		resultcode = -1;
	};

	if (argvpath)
		delete [] argvpath;

	appdic.CleanupObjects( 3, &app_info, &newdb, &olddb );

	XCDeleteFile( lockfilename );

	Protea::Uninitialize();

	if (resultcode)
		cout << endl << "Prep completed unsuccessfully." << endl;
	else
		cout << endl << "Prep completed successfully." << endl;

	return resultcode;
};
